Fix references.
[dragonfly.git] / usr.sbin / pkg_install / sign / x509.c
1 /*-
2  * Copyright (c) 2000 Softweyr LLC, South Jordan, Utah, USA.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY Softweyr LLC ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL Softweyr LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD: src/usr.sbin/pkg_install/sign/x509.c,v 1.4 2004/06/29 19:06:42 eik Exp $
27  * $DragonFly: src/usr.sbin/pkg_install/sign/Attic/x509.c,v 1.4 2004/07/30 04:46:14 dillon Exp $
28  */
29
30 #include <sys/types.h>
31 #include <sys/wait.h>
32
33 #include <assert.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <libgen.h>
39
40 #include <openssl/rsa.h>
41 #include <openssl/evp.h>
42 #include <openssl/objects.h>
43 #include <openssl/x509.h>
44 #include <openssl/err.h>
45 #include <openssl/pem.h>
46 #include <openssl/ssl.h>
47
48 #include "stand.h"
49 #include "gzip.h"
50 #include "extern.h"
51
52
53 /*
54  * Default names for the signing key and certificate(s) for verification.
55  */
56 #define CERTFILE "/etc/ssl/pkg.crt"
57 #define KEYFILE  "/etc/ssl/pkg.key"
58
59
60 /*
61  * Private context for X.509 signature checker
62  */
63 struct x509_checker
64 {
65         const char * id;
66         const char * filename;
67
68         struct signature * signature;
69
70         STACK_OF(X509) * certs;
71         EVP_MD_CTX rsa_ctx, dsa_ctx;
72         int has_rsa, has_dsa;
73 };
74
75
76 static void key_from_name(char *, const char *);
77
78 /*
79  * Initialize an X.509 "checker" context.
80  */
81 void *
82 new_x509_checker(h, sign, userid, envp, filename)
83         struct mygzip_header *h;
84         struct signature *sign;
85         const char *userid;     
86         char *envp[];
87         /*@observer@*/const char *filename;
88 {
89         FILE * fp;
90         struct x509_checker * me;
91         char certfile[PATH_MAX + 1] = CERTFILE;
92         X509 * x509;
93
94         assert(sign->type == TAG_X509);
95
96         /*
97          * Make sure data conforms to what we can handle.  We do not write a
98          * trailing null onto the signature like some other types, because
99          * the X.509 signature is binary data.
100          */
101         if (sign->length > MAXID) {
102             warnx("Corrupted X.509 header in %s", filename);
103             return 0;
104         }
105
106         me = malloc(sizeof *me);
107         if (me == NULL) {
108             warn("Cannot allocate x509_checker");
109             return 0;
110         }
111         me->id = sign->data;
112         me->filename = filename;
113         me->signature = sign;
114         me->has_rsa = 0;
115         me->has_dsa = 0;
116
117         key_from_name(certfile, userkey);
118
119         /*
120          * Load just the crypto library error strings.
121          */
122         ERR_load_crypto_strings();
123
124         /*
125          * Load the stack of X.509 certs we will compare against.
126          *
127          * KLUDGE: this needs to be fleshed out a bit.  We can do better
128          * than hard-coding the location of the cert key file.
129          */
130         me->certs = sk_X509_new_null();
131
132         fp = fopen(certfile, "r");
133         if (fp == NULL) {
134             warnx("Cannot open public key %s", certfile);
135             return 0;
136         }
137
138         if (verbose)
139             printf("Loading certificates from %s:\n", certfile);
140
141         while ((x509 = PEM_read_X509(fp, NULL, NULL, 0)))       {
142             sk_X509_push(me->certs, x509);
143
144             switch (EVP_PKEY_type(X509_get_pubkey(x509)->type))
145             {
146             case EVP_PKEY_RSA:
147                 me->has_rsa = 1;
148                 break;
149
150             case EVP_PKEY_DSA:
151                 me->has_dsa = 1;
152                 break;
153
154             default:
155                 warnx("Uknown certificate type");
156                 return 0;
157             }
158
159             /*
160              * By default, print the contents of the cert we matched so the
161              * user can decide if she is willing to accept a package from
162              * whoever signed this.
163              */
164             if (!quiet)
165                 X509_print_fp(stdout, x509);
166         }
167         fclose(fp);
168   
169         /*
170          * Initialize the verification contexts for both RSA and DSA.
171          */
172         if (me->has_rsa) EVP_VerifyInit(&me->rsa_ctx, EVP_sha1());
173         if (me->has_dsa) EVP_VerifyInit(&me->dsa_ctx, EVP_dss1());
174
175         return me;
176 }
177
178
179 /*
180  * "Add" another data block to an existing checker.
181  */
182 void 
183 x509_add(arg, buffer, length)
184         void *arg;
185         const char *buffer;
186         size_t length;
187 {
188         struct x509_checker * me = arg;
189
190         if (me->has_rsa) EVP_VerifyUpdate(&me->rsa_ctx, buffer, length);
191         if (me->has_dsa) EVP_VerifyUpdate(&me->dsa_ctx, buffer, length);
192 }
193
194
195 /*
196  * Finalize an existing checker and verify the signature matches one of the
197  * certs in our stack.
198  */
199 int
200 x509_sign_ok(arg)
201         void *arg;
202 {
203         struct x509_checker * n = arg;
204         X509 * x509;
205         EVP_PKEY * pkey;
206         EVP_MD_CTX * md_ctx;
207         int status;
208
209         if (verbose)
210             printf("\n\n-------\n\nChecking package signature:\n");
211
212         while ((x509 = sk_X509_pop(n->certs)) != NULL) {
213             /*
214              * Get public key from cert.
215              */
216             pkey = X509_get_pubkey(x509);
217             if (pkey == NULL) {
218                 warnx("Getting public key:");
219                 ERR_print_errors_fp(stderr);
220                 continue;
221             }
222
223             if (verbose)
224                 X509_print_fp(stdout, x509);
225
226             switch (EVP_PKEY_type(pkey->type))
227             {
228             case EVP_PKEY_RSA:
229                 md_ctx = &n->rsa_ctx;
230                 break;
231
232             case EVP_PKEY_DSA:
233                 md_ctx = &n->dsa_ctx;
234                 break;
235
236             default:
237                 break;
238             }
239
240             status = EVP_VerifyFinal(md_ctx,
241                                      n->signature->data,
242                                      n->signature->length,
243                                      pkey);
244
245             EVP_PKEY_free(pkey);
246             X509_free(x509);
247
248             if (status == 1) {
249                 fprintf(stderr, "X.509 signature matched\n");
250
251                 /*
252                  * KLUDGE: Does this free the rest of the certs, or just the
253                  * stack itself?  Enquiring minds want to know.
254                  */
255                 sk_X509_free(n->certs);
256                 return PKG_GOODSIG;
257             }
258         }
259
260         warnx("Verifying signature:");
261         ERR_print_errors_fp(stderr);
262         sk_X509_free(n->certs);
263         return PKG_BADSIG;
264 }
265
266
267 /*
268  * Sign the specified filename into sign.
269  */
270 int 
271 retrieve_x509_marker(filename, sign, userid)
272         const char * filename;
273         struct signature ** sign;
274         const char * userid;
275 {
276         struct signature * n;
277         struct mygzip_header h;
278         FILE * f, * keyf;
279         char buffer[1024];
280         ssize_t length;
281         int err;
282         int sig_len = 4096;
283         unsigned char * sig_buf;
284         EVP_MD_CTX md_ctx;
285         const EVP_MD * md_type;
286         EVP_PKEY * pkey;
287
288         char keyfile[PATH_MAX + 1] = KEYFILE;
289
290         key_from_name(keyfile, userkey);
291
292         f = fopen(filename, "r");
293         if (f == NULL) {
294             free(n);
295             return 0;
296         }
297         if (gzip_read_header(f, &h, sign) == GZIP_NOT_GZIP) {
298             warnx("File %s is not a gzip file\n", filename);
299             fclose(f);
300             free(n);
301             return 0;
302         }
303
304         /*
305          * Sign the remaining data:
306          * Load just the crypto library error strings.
307          */
308         ERR_load_crypto_strings();
309         
310         /*
311          * Read private key.
312          */
313         keyf = fopen(keyfile, "r");
314         if (keyf == NULL)
315         {
316             warnx("Cannot open private key %s.", keyfile);
317             return 0;
318         }
319         
320         pkey = PEM_read_PrivateKey(keyf, NULL, NULL, 0);
321         fclose(keyf);
322         
323         if (pkey == NULL)
324         { 
325             warnx("Reading private key %s:", keyfile);
326             ERR_print_errors_fp(stderr);
327             return 0;
328         }
329         
330         /*
331          * Do the signature.  The remaining bytes of the GZIP file are the
332          * compressed tar image, which is what we are signing.
333          */
334         switch (EVP_PKEY_type(pkey->type))
335         {
336         case EVP_PKEY_RSA:
337             md_type = EVP_sha1();
338 printf("*** It's an RSA key.\n");
339             break;
340
341         case EVP_PKEY_DSA:
342             md_type = EVP_dss1();
343 printf("@@@ It's a DSA key, yippee!\n");
344             break;
345
346         default:
347             warnx("Uknown key type");
348             return 0;
349         }
350
351         EVP_SignInit(&md_ctx, md_type);
352
353         while ((length = fread(buffer, 1, sizeof buffer, f)) > 0)
354                 EVP_SignUpdate(&md_ctx, buffer, length);
355
356         sig_buf = malloc(sig_len);
357         if (sig_buf == NULL) {
358             warnx("Cannot allocated %u bytes for signature buffer", sig_len);
359             return 0;
360         }
361
362         err = EVP_SignFinal(&md_ctx, sig_buf, &sig_len, pkey);
363         
364         if (err != 1)
365         {
366             warnx("Creating signature:");
367             ERR_print_errors_fp(stderr);
368             return 0;
369         }
370         
371         EVP_PKEY_free(pkey);
372         
373         /*
374          * Stuff the signature onto the head of the chain of signatures in
375          * the package.
376          */
377         n = malloc(sizeof *n);
378         if (n == NULL) {
379             warnx("Cannot allocate %u bytes for new signature", sizeof *n);
380             return 0;
381         }
382         n->data = sig_buf;
383         n->length = sig_len;
384         n->type = TAG_X509;
385         memcpy(n->tag, x509tag, sizeof x509tag);
386         sign_fill_tag(n);
387         n->next = *sign;
388         *sign = n;
389
390         /*
391          * Report our success.
392          */
393         return 1;
394 }
395
396
397 static void
398 key_from_name(char * filename, const char * ident)
399 {
400         char * cp;
401
402         /*
403          * If an alternate keyfile was specified, treat it as the name of an
404          * alternate private key with which to sign or verify the package.
405          */
406         if (ident) {
407             printf("Using alternate key/cert \"%s\".\n", ident);
408             if (strchr(ident, '/')) {
409                 /*
410                  * The user specified a path, take it verbatim.
411                  */
412                 strncpy(filename, ident, PATH_MAX);
413             } else {
414                 cp = dirname(filename);
415                 if (cp == NULL) {
416                     warnx("Key directory not correctly specified.");
417                     return;
418                 }
419                 snprintf(filename, PATH_MAX, "%s/%s", cp, ident);
420             }
421         }
422
423         if (verbose)
424             printf("Key is \"%s\".\n", filename);
425 }