]> sourceware.org Git - systemtap.git/blob - modsign.cxx
Consolidate task_finder/vma tracker initialization.
[systemtap.git] / modsign.cxx
1 /*
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
4 output file.
5
6 Copyright (C) 2009 Red Hat Inc.
7
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.
12
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.
17
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
21 */
22
23 #include "util.h"
24 #include <iostream>
25 #include <string>
26
27 extern "C" {
28 #include "nsscommon.h"
29
30 #include <nspr.h>
31 #include <nss.h>
32 #include <pk11pub.h>
33 #include <cryptohi.h>
34
35 #include <stdio.h>
36 #include <stdlib.h>
37
38 #include <unistd.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <pwd.h>
42 }
43
44 using namespace std;
45
46 /* Function: int init_cert_db_path (const string &cert_db_path);
47 *
48 * Initialize a certificate database at the given path.
49 */
50 static int
51 init_cert_db_path (const string &cert_db_path) {
52 int rc;
53
54 // Generate the certificate and database.
55 string cmd = PKGLIBDIR "/stap-gen-cert " + cert_db_path;
56 rc = system (cmd.c_str ()) == 0;
57
58 return rc;
59 }
60
61 /* Function: int check_cert_db_path (const string &cert_db_path);
62 *
63 * Check that the given certificate directory exists and is initialized.
64 * Create and/or initialize it otherwise.
65 */
66 static int
67 check_cert_db_path (const string &cert_db_path) {
68 // Does the path exist?
69 PRFileInfo fileInfo;
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);
73
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)
78 {
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 ());
83 else
84 PR_Delete (fname.c_str ());
85 }
86
87 return 1; // ok
88 }
89
90 /* Function: char * password_callback()
91 *
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.
96 */
97 static char *
98 password_callback (PK11SlotInfo *info, PRBool retry, void *arg)
99 {
100 char *passwd = NULL;
101
102 if (! retry && arg)
103 passwd = PORT_Strdup((char *)arg);
104
105 return passwd;
106 }
107
108 /* Obtain the certificate and key database password from the given file. */
109 static char *
110 get_password (const string &fileName)
111 {
112 PRFileDesc *local_file_fd;
113 PRFileInfo fileInfo;
114 PRInt32 numBytesRead;
115 PRStatus prStatus;
116 PRInt32 i;
117 char *password;
118
119 prStatus = PR_GetFileInfo (fileName.c_str(), &fileInfo);
120 if (prStatus != PR_SUCCESS || fileInfo.type != PR_FILE_FILE || fileInfo.size < 0)
121 {
122 cerr << "Could not obtain information on password file " << fileName << "." << endl;
123 nssError ();
124 return NULL;
125 }
126
127 local_file_fd = PR_Open (fileName.c_str(), PR_RDONLY, 0);
128 if (local_file_fd == NULL)
129 {
130 cerr << "Could not open password file " << fileName << "." << endl;
131 nssError ();
132 return NULL;
133 }
134
135 password = (char*)PORT_Alloc (fileInfo.size + 1);
136 if (! password)
137 {
138 cerr << "Unable to allocate " << (fileInfo.size + 1) << " bytes." << endl;
139 nssError ();
140 return NULL;
141 }
142
143 numBytesRead = PR_Read (local_file_fd, password, fileInfo.size);
144 if (numBytesRead <= 0)
145 {
146 cerr << "Error reading password file " << fileName << "." << endl;
147 nssError ();
148 return 0;
149 }
150
151 PR_Close (local_file_fd);
152
153 /* Keep only the first line of data. */
154 for (i = 0; i < numBytesRead; ++i)
155 {
156 if (password[i] == '\n' || password[i] == '\r' || password[i] == '\0')
157 break;
158 }
159 password[i] = '\0';
160
161 return password;
162 }
163
164 static void
165 sign_it (const string &inputName, const string &outputName, SECKEYPrivateKey *privKey)
166 {
167 unsigned char buffer[4096];
168 PRFileDesc *local_file_fd;
169 PRUint32 numBytes;
170 SECStatus secStatus;
171 SGNContext *sgn;
172 SECItem signedData;
173
174 /* Set up the signing context. */
175 sgn = SGN_NewContext (SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, privKey);
176 if (! sgn)
177 {
178 cerr << "Could not create signing context." << endl;
179 nssError ();
180 return;
181 }
182 secStatus = SGN_Begin (sgn);
183 if (secStatus != SECSuccess)
184 {
185 cerr << "Could not initialize signing context." << endl;
186 nssError ();
187 return;
188 }
189
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)
193 {
194 cerr << "Could not open module file " << inputName << "." << endl;
195 nssError ();
196 return;
197 }
198
199 for (;;)
200 {
201 numBytes = PR_Read (local_file_fd, buffer, sizeof (buffer));
202 if (numBytes == 0)
203 break; /* EOF */
204
205 if (numBytes < 0)
206 {
207 cerr << "Error reading module file " << inputName << "." << endl;
208 nssError ();
209 return;
210 }
211
212 /* Add the data to the signature. */
213 secStatus = SGN_Update (sgn, buffer, numBytes);
214 if (secStatus != SECSuccess)
215 {
216 cerr << "Error while signing module file " << inputName << "." << endl;
217 nssError ();
218 return;
219 }
220 }
221
222 PR_Close (local_file_fd);
223
224 /* Complete the signature. */
225 secStatus = SGN_End (sgn, & signedData);
226 if (secStatus != SECSuccess)
227 {
228 cerr << "Could not complete signature of module file " << inputName << "." << endl;
229 nssError ();
230 return;
231 }
232
233 SGN_DestroyContext (sgn, PR_TRUE);
234
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)
239 {
240 cerr << "Could not open signature file " << outputName << "." << endl;
241 nssError ();
242 return;
243 }
244
245 numBytes = PR_Write (local_file_fd, signedData.data, signedData.len);
246 if (numBytes < 0 || numBytes != signedData.len)
247 {
248 cerr << "Error writing to signature file " << outputName << "." << endl;
249 nssError ();
250 return;
251 }
252
253 PR_Close (local_file_fd);
254 }
255
256 int
257 main(int argc, char **argv)
258 {
259 const char *nickName = "stap-server";
260 string module_name;
261 string cert_db_path;
262 char *password;
263 CERTCertificate *cert;
264 SECKEYPrivateKey *privKey;
265 SECStatus secStatus;
266 const char *stap_dir;
267 struct passwd *pwd;
268
269 if (argc < 2) {
270 cerr << "Module name was not specified." << endl;
271 return 1;
272 }
273 module_name = argv[1];
274
275 if (argc >= 3)
276 cert_db_path = argv[2];
277 else {
278 // Use the default database for this user.
279 if (geteuid () == 0)
280 cert_db_path = SYSCONFDIR "/systemtap/ssl/server";
281 else {
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());
287 if (pwd)
288 stap_dir = pwd->pw_dir;
289 else {
290 cerr << "Unable to determine the certificate database path." << endl;
291 return 1;
292 }
293 }
294 }
295 cert_db_path = stap_dir;
296 cert_db_path += "/.systemtap/ssl/server";
297 }
298 }
299
300 if (! check_cert_db_path (cert_db_path))
301 return 1;
302
303 password = get_password (cert_db_path + "/pw");
304 if (! password)
305 {
306 cerr << "Unable to obtain certificate database password." << endl;
307 return 1;
308 }
309
310 /* Call the NSPR initialization routines. */
311 PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
312
313 /* Set the cert database password callback. */
314 PK11_SetPasswordFunc (password_callback);
315
316 /* Initialize NSS. */
317 secStatus = NSS_Init (cert_db_path.c_str());
318 if (secStatus != SECSuccess)
319 {
320 cerr << "Unable to initialize nss library." << endl;
321 nssError ();
322 return 1;
323 }
324
325 /* Get own certificate and private key. */
326 cert = PK11_FindCertFromNickname (nickName, password);
327 if (cert == NULL)
328 {
329 cerr << "Unable to find certificate with nickname " << nickName
330 << " in " << cert_db_path << "." << endl;
331 nssError ();
332 return 1;
333 }
334
335 privKey = PK11_FindKeyByAnyCert (cert, password);
336 if (privKey == NULL)
337 {
338 cerr << "Unable to obtain private key from the certificate with nickname " << nickName
339 << " in " << cert_db_path << "." << endl;
340 nssError ();
341 return 1;
342 }
343
344 /* Sign the file. */
345 sign_it (module_name, module_name + ".sgn", privKey);
346
347 /* Shutdown NSS and exit NSPR gracefully. */
348 nssCleanup ();
349
350 return 0;
351 }
352
353 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.087867 seconds and 5 git commands to generate.