2 This program signs the given file using the named certificate and private
3 key in the given certificate database and places the signature in the named
6 Copyright (C) 2009 Red Hat Inc.
8 This file is part of systemtap, and is free software. You can
9 redistribute it and/or modify it under the terms of the GNU General Public
10 License as published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "nsscommon.h"
40 #include <sys/types.h>
46 /* Function: int init_cert_db_path (const string &cert_db_path);
48 * Initialize a certificate database at the given path.
51 init_cert_db_path (const string
&cert_db_path
) {
54 // Generate the certificate and database.
55 string cmd
= PKGLIBDIR
"/stap-gen-cert " + cert_db_path
;
56 rc
= system (cmd
.c_str ()) == 0;
61 /* Function: int check_cert_db_path (const string &cert_db_path);
63 * Check that the given certificate directory exists and is initialized.
64 * Create and/or initialize it otherwise.
67 check_cert_db_path (const string
&cert_db_path
) {
68 // Does the path exist?
70 PRStatus prStatus
= PR_GetFileInfo (cert_db_path
.c_str(), &fileInfo
);
71 if (prStatus
!= PR_SUCCESS
|| fileInfo
.type
!= PR_FILE_DIRECTORY
)
72 return init_cert_db_path (cert_db_path
);
74 // Update the user's cert file if it is old.
75 string fname
= cert_db_path
+ "/stap-server.cert";
76 prStatus
= PR_GetFileInfo (fname
.c_str (), &fileInfo
);
77 if (prStatus
== PR_SUCCESS
&& fileInfo
.type
== PR_FILE_FILE
&& fileInfo
.size
> 0)
79 string fname1
= cert_db_path
+ "/stap.cert";
80 prStatus
= PR_GetFileInfo (fname1
.c_str (), &fileInfo
);
81 if (prStatus
!= PR_SUCCESS
)
82 PR_Rename (fname
.c_str (), fname1
.c_str ());
84 PR_Delete (fname
.c_str ());
90 /* Function: char * password_callback()
92 * Purpose: This function is our custom password handler that is called by
93 * NSS when retrieving private certs and keys from the database. Returns a
94 * pointer to a string that with a password for the database. Password pointer
95 * should point to dynamically allocated memory that will be freed later.
98 password_callback (PK11SlotInfo
*info
, PRBool retry
, void *arg
)
103 passwd
= PORT_Strdup((char *)arg
);
108 /* Obtain the certificate and key database password from the given file. */
110 get_password (const string
&fileName
)
112 PRFileDesc
*local_file_fd
;
114 PRInt32 numBytesRead
;
119 prStatus
= PR_GetFileInfo (fileName
.c_str(), &fileInfo
);
120 if (prStatus
!= PR_SUCCESS
|| fileInfo
.type
!= PR_FILE_FILE
|| fileInfo
.size
< 0)
122 cerr
<< "Could not obtain information on password file " << fileName
<< "." << endl
;
127 local_file_fd
= PR_Open (fileName
.c_str(), PR_RDONLY
, 0);
128 if (local_file_fd
== NULL
)
130 cerr
<< "Could not open password file " << fileName
<< "." << endl
;
135 password
= (char*)PORT_Alloc (fileInfo
.size
+ 1);
138 cerr
<< "Unable to allocate " << (fileInfo
.size
+ 1) << " bytes." << endl
;
143 numBytesRead
= PR_Read (local_file_fd
, password
, fileInfo
.size
);
144 if (numBytesRead
<= 0)
146 cerr
<< "Error reading password file " << fileName
<< "." << endl
;
151 PR_Close (local_file_fd
);
153 /* Keep only the first line of data. */
154 for (i
= 0; i
< numBytesRead
; ++i
)
156 if (password
[i
] == '\n' || password
[i
] == '\r' || password
[i
] == '\0')
165 sign_it (const string
&inputName
, const string
&outputName
, SECKEYPrivateKey
*privKey
)
167 unsigned char buffer
[4096];
168 PRFileDesc
*local_file_fd
;
174 /* Set up the signing context. */
175 sgn
= SGN_NewContext (SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION
, privKey
);
178 cerr
<< "Could not create signing context." << endl
;
182 secStatus
= SGN_Begin (sgn
);
183 if (secStatus
!= SECSuccess
)
185 cerr
<< "Could not initialize signing context." << endl
;
190 /* Now read the data and add it to the signature. */
191 local_file_fd
= PR_Open (inputName
.c_str(), PR_RDONLY
, 0);
192 if (local_file_fd
== NULL
)
194 cerr
<< "Could not open module file " << inputName
<< "." << endl
;
201 numBytes
= PR_Read (local_file_fd
, buffer
, sizeof (buffer
));
207 cerr
<< "Error reading module file " << inputName
<< "." << endl
;
212 /* Add the data to the signature. */
213 secStatus
= SGN_Update (sgn
, buffer
, numBytes
);
214 if (secStatus
!= SECSuccess
)
216 cerr
<< "Error while signing module file " << inputName
<< "." << endl
;
222 PR_Close (local_file_fd
);
224 /* Complete the signature. */
225 secStatus
= SGN_End (sgn
, & signedData
);
226 if (secStatus
!= SECSuccess
)
228 cerr
<< "Could not complete signature of module file " << inputName
<< "." << endl
;
233 SGN_DestroyContext (sgn
, PR_TRUE
);
235 /* Now write the signed data to the output file. */
236 local_file_fd
= PR_Open (outputName
.c_str(), PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
,
237 PR_IRUSR
| PR_IWUSR
| PR_IRGRP
| PR_IWGRP
| PR_IROTH
);
238 if (local_file_fd
== NULL
)
240 cerr
<< "Could not open signature file " << outputName
<< "." << endl
;
245 numBytes
= PR_Write (local_file_fd
, signedData
.data
, signedData
.len
);
246 if (numBytes
< 0 || numBytes
!= signedData
.len
)
248 cerr
<< "Error writing to signature file " << outputName
<< "." << endl
;
253 PR_Close (local_file_fd
);
257 main(int argc
, char **argv
)
259 const char *nickName
= "stap-server";
263 CERTCertificate
*cert
;
264 SECKEYPrivateKey
*privKey
;
266 const char *stap_dir
;
270 cerr
<< "Module name was not specified." << endl
;
273 module_name
= argv
[1];
276 cert_db_path
= argv
[2];
278 // Use the default database for this user.
280 cert_db_path
= SYSCONFDIR
"/systemtap/ssl/server";
282 stap_dir
= getenv ("SYSTEMTAP_DIR");
283 if (stap_dir
== NULL
) {
284 stap_dir
= getenv("HOME");
285 if (stap_dir
== NULL
) {
286 pwd
= getpwuid(getuid());
288 stap_dir
= pwd
->pw_dir
;
290 cerr
<< "Unable to determine the certificate database path." << endl
;
295 cert_db_path
= stap_dir
;
296 cert_db_path
+= "/.systemtap/ssl/server";
300 if (! check_cert_db_path (cert_db_path
))
303 password
= get_password (cert_db_path
+ "/pw");
306 cerr
<< "Unable to obtain certificate database password." << endl
;
310 /* Call the NSPR initialization routines. */
311 PR_Init (PR_SYSTEM_THREAD
, PR_PRIORITY_NORMAL
, 1);
313 /* Set the cert database password callback. */
314 PK11_SetPasswordFunc (password_callback
);
316 /* Initialize NSS. */
317 secStatus
= NSS_Init (cert_db_path
.c_str());
318 if (secStatus
!= SECSuccess
)
320 cerr
<< "Unable to initialize nss library." << endl
;
325 /* Get own certificate and private key. */
326 cert
= PK11_FindCertFromNickname (nickName
, password
);
329 cerr
<< "Unable to find certificate with nickname " << nickName
330 << " in " << cert_db_path
<< "." << endl
;
335 privKey
= PK11_FindKeyByAnyCert (cert
, password
);
338 cerr
<< "Unable to obtain private key from the certificate with nickname " << nickName
339 << " in " << cert_db_path
<< "." << endl
;
345 sign_it (module_name
, module_name
+ ".sgn", privKey
);
347 /* Shutdown NSS and exit NSPR gracefully. */
353 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */