Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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.1.2.2 2002/08/20 06:35:08 obrien Exp $
27  * $DragonFly: src/usr.sbin/pkg_install/sign/Attic/x509.c,v 1.2 2003/06/17 04:29:59 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         char * cp;
93         X509 * x509;
94
95         assert(sign->type == TAG_X509);
96
97         /*
98          * Make sure data conforms to what we can handle.  We do not write a
99          * trailing null onto the signature like some other types, because
100          * the X.509 signature is binary data.
101          */
102         if (sign->length > MAXID) {
103             warnx("Corrupted X.509 header in %s", filename);
104             return 0;
105         }
106
107         me = malloc(sizeof *me);
108         if (me == NULL) {
109             warn("Cannot allocate x509_checker");
110             return 0;
111         }
112         me->id = sign->data;
113         me->filename = filename;
114         me->signature = sign;
115         me->has_rsa = 0;
116         me->has_dsa = 0;
117
118         key_from_name(certfile, userkey);
119
120         /*
121          * Load just the crypto library error strings.
122          */
123         ERR_load_crypto_strings();
124
125         /*
126          * Load the stack of X.509 certs we will compare against.
127          *
128          * KLUDGE: this needs to be fleshed out a bit.  We can do better
129          * than hard-coding the location of the cert key file.
130          */
131         me->certs = sk_X509_new_null();
132
133         fp = fopen(certfile, "r");
134         if (fp == NULL) {
135             warnx("Cannot open public key %s", certfile);
136             return 0;
137         }
138
139         if (verbose)
140             printf("Loading certificates from %s:\n", certfile);
141
142         while (x509 = PEM_read_X509(fp, NULL, NULL, 0)) {
143             sk_X509_push(me->certs, x509);
144
145             switch (EVP_PKEY_type(X509_get_pubkey(x509)->type))
146             {
147             case EVP_PKEY_RSA:
148                 me->has_rsa = 1;
149                 break;
150
151             case EVP_PKEY_DSA:
152                 me->has_dsa = 1;
153                 break;
154
155             default:
156                 warnx("Uknown certificate type");
157                 return 0;
158             }
159
160             /*
161              * By default, print the contents of the cert we matched so the
162              * user can decide if she is willing to accept a package from
163              * whoever signed this.
164              */
165             if (!quiet)
166                 X509_print_fp(stdout, x509);
167         }
168         fclose(fp);
169   
170         /*
171          * Initialize the verification contexts for both RSA and DSA.
172          */
173         if (me->has_rsa) EVP_VerifyInit(&me->rsa_ctx, EVP_sha1());
174         if (me->has_dsa) EVP_VerifyInit(&me->dsa_ctx, EVP_dss1());
175
176         return me;
177 }
178
179
180 /*
181  * "Add" another data block to an existing checker.
182  */
183 void 
184 x509_add(arg, buffer, length)
185         void *arg;
186         const char *buffer;
187         size_t length;
188 {
189         struct x509_checker * me = arg;
190
191         if (me->has_rsa) EVP_VerifyUpdate(&me->rsa_ctx, buffer, length);
192         if (me->has_dsa) EVP_VerifyUpdate(&me->dsa_ctx, buffer, length);
193 }
194
195
196 /*
197  * Finalize an existing checker and verify the signature matches one of the
198  * certs in our stack.
199  */
200 int
201 x509_sign_ok(arg)
202         void *arg;
203 {
204         struct x509_checker * n = arg;
205         X509 * x509;
206         EVP_PKEY * pkey;
207         EVP_MD_CTX * md_ctx;
208         int status;
209
210         if (verbose)
211             printf("\n\n-------\n\nChecking package signature:\n");
212
213         while ((x509 = sk_X509_pop(n->certs)) != NULL) {
214             /*
215              * Get public key from cert.
216              */
217             pkey = X509_get_pubkey(x509);
218             if (pkey == NULL) {
219                 warnx("Getting public key:");
220                 ERR_print_errors_fp(stderr);
221                 continue;
222             }
223
224             if (verbose)
225                 X509_print_fp(stdout, x509);
226
227             switch (EVP_PKEY_type(pkey->type))
228             {
229             case EVP_PKEY_RSA:
230                 md_ctx = &n->rsa_ctx;
231                 break;
232
233             case EVP_PKEY_DSA:
234                 md_ctx = &n->dsa_ctx;
235                 break;
236
237             default:
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         EVP_MD * md_type;
286         EVP_PKEY * pkey;
287
288         char keyfile[PATH_MAX + 1] = KEYFILE;
289         char * kp;
290
291         key_from_name(keyfile, userkey);
292
293         f = fopen(filename, "r");
294         if (f == NULL) {
295             free(n);
296             return 0;
297         }
298         if (gzip_read_header(f, &h, sign) == GZIP_NOT_GZIP) {
299             warnx("File %s is not a gzip file\n", filename);
300             fclose(f);
301             free(n);
302             return 0;
303         }
304
305         /*
306          * Sign the remaining data:
307          * Load just the crypto library error strings.
308          */
309         ERR_load_crypto_strings();
310         
311         /*
312          * Read private key.
313          */
314         keyf = fopen(keyfile, "r");
315         if (keyf == NULL)
316         {
317             warnx("Cannot open private key %s.", keyfile);
318             return 0;
319         }
320         
321         pkey = PEM_read_PrivateKey(keyf, NULL, NULL, 0);
322         fclose(keyf);
323         
324         if (pkey == NULL)
325         { 
326             warnx("Reading private key %s:", keyfile);
327             ERR_print_errors_fp(stderr);
328             return 0;
329         }
330         
331         /*
332          * Do the signature.  The remaining bytes of the GZIP file are the
333          * compressed tar image, which is what we are signing.
334          */
335         switch (EVP_PKEY_type(pkey->type))
336         {
337         case EVP_PKEY_RSA:
338             md_type = EVP_sha1();
339 printf("*** It's an RSA key.\n");
340             break;
341
342         case EVP_PKEY_DSA:
343             md_type = EVP_dss1();
344 printf("@@@ It's a DSA key, yippee!\n");
345             break;
346
347         default:
348             warnx("Uknown key type");
349             return 0;
350         }
351
352         EVP_SignInit(&md_ctx, md_type);
353
354         while ((length = fread(buffer, 1, sizeof buffer, f)) > 0)
355                 EVP_SignUpdate(&md_ctx, buffer, length);
356
357         sig_buf = malloc(sig_len);
358         if (sig_buf == NULL) {
359             warnx("Cannot allocated %u bytes for signature buffer", sig_len);
360             return 0;
361         }
362
363         err = EVP_SignFinal(&md_ctx, sig_buf, &sig_len, pkey);
364         
365         if (err != 1)
366         {
367             warnx("Creating signature:");
368             ERR_print_errors_fp(stderr);
369             return 0;
370         }
371         
372         EVP_PKEY_free(pkey);
373         
374         /*
375          * Stuff the signature onto the head of the chain of signatures in
376          * the package.
377          */
378         n = malloc(sizeof *n);
379         if (n == NULL) {
380             warnx("Cannot allocate %u bytes for new signature", sizeof *n);
381             return 0;
382         }
383         n->data = sig_buf;
384         n->length = sig_len;
385         n->type = TAG_X509;
386         memcpy(n->tag, x509tag, sizeof x509tag);
387         sign_fill_tag(n);
388         n->next = *sign;
389         *sign = n;
390
391         /*
392          * Report our success.
393          */
394         return 1;
395 }
396
397
398 static void
399 key_from_name(char * filename, const char * ident)
400 {
401         char * cp;
402
403         /*
404          * If an alternate keyfile was specified, treat it as the name of an
405          * alternate private key with which to sign or verify the package.
406          */
407         if (ident) {
408             printf("Using alternate key/cert \"%s\".\n", ident);
409             if (strchr(ident, '/')) {
410                 /*
411                  * The user specified a path, take it verbatim.
412                  */
413                 strncpy(filename, ident, PATH_MAX);
414             } else {
415                 cp = dirname(filename);
416                 if (cp == NULL) {
417                     warnx("Key directory not correctly specified.");
418                     return;
419                 }
420                 snprintf(filename, PATH_MAX, "%s/%s", cp, ident);
421             }
422         }
423
424         if (verbose)
425             printf("Key is \"%s\".\n", filename);
426 }