1 /*
2 * Copyright 2011-2013 Red Hat, Inc.
3 * All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author(s): Peter Jones <pjones@redhat.com>
18 */
19
20 #include <assert.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <time.h>
24 #include <unistd.h>
25 #include <stdarg.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <syslog.h>
29
30 #include "pesign.h"
31
32 #include <prerror.h>
33 #include <nss.h>
34 #include <secport.h>
35 #include <secpkcs7.h>
36 #include <secder.h>
37 #include <keyhi.h>
38 #include <base64.h>
39 #include <pk11pub.h>
40 #include <secerr.h>
41 #include <certt.h>
42
43 struct digest_param {
44 char *name;
45 SECOidTag digest_tag;
46 SECOidTag signature_tag;
47 SECOidTag digest_encryption_tag;
48 const efi_guid_t *efi_guid;
49 int size;
50 };
51
52 static struct digest_param digest_params[] = {
53 {.name = "sha256",
54 .digest_tag = SEC_OID_SHA256,
55 .signature_tag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION,
56 .digest_encryption_tag = SEC_OID_PKCS1_RSA_ENCRYPTION,
57 .efi_guid = &efi_guid_sha256,
58 .size = 32
59 },
60 #if 1
61 {.name = "sha1",
62 .digest_tag = SEC_OID_SHA1,
63 .signature_tag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION,
64 .digest_encryption_tag = SEC_OID_PKCS1_RSA_ENCRYPTION,
65 .efi_guid = &efi_guid_sha1,
66 .size = 20
67 },
68 #endif
69 };
70 static int n_digest_params = sizeof (digest_params) / sizeof (digest_params[0]);
71
72 SECOidTag
digest_get_digest_oid(cms_context * cms)73 digest_get_digest_oid(cms_context *cms)
74 {
75 int i = cms->selected_digest;
76 return digest_params[i].digest_tag;
77 }
78
79 SECOidTag
digest_get_encryption_oid(cms_context * cms)80 digest_get_encryption_oid(cms_context *cms)
81 {
82 int i = cms->selected_digest;
83 return digest_params[i].digest_encryption_tag;
84 }
85
86 SECOidTag
digest_get_signature_oid(cms_context * cms)87 digest_get_signature_oid(cms_context *cms)
88 {
89 int i = cms->selected_digest;
90 return digest_params[i].signature_tag;
91 }
92
93 int
digest_get_digest_size(cms_context * cms)94 digest_get_digest_size(cms_context *cms)
95 {
96 int i = cms->selected_digest;
97 return digest_params[i].size;
98 }
99
100 void
teardown_digests(cms_context * ctx)101 teardown_digests(cms_context *ctx)
102 {
103 struct digest *digests = ctx->digests;
104
105 if (!digests)
106 return;
107
108 for (int i = 0; i < n_digest_params; i++) {
109 if (digests[i].pk11ctx) {
110 PK11_Finalize(digests[i].pk11ctx);
111 PK11_DestroyContext(digests[i].pk11ctx, PR_TRUE);
112 }
113 if (digests[i].pe_digest) {
114 /* XXX sure seems like we should be freeing it here,
115 * but that's segfaulting, and we know it'll get
116 * cleaned up with PORT_FreeArena a couple of lines
117 * down.
118 */
119 digests[i].pe_digest = NULL;
120 }
121 }
122 PORT_Free(digests);
123 ctx->digests = NULL;
124 }
125
126 static int
127 __attribute__ ((format (printf, 3, 4)))
cms_common_log(cms_context * ctx,int priority,char * fmt,...)128 cms_common_log(cms_context *ctx, int priority, char *fmt, ...)
129 {
130 va_list ap;
131 FILE *out = priority & LOG_ERR ? stderr : stdout;
132
133 va_start(ap, fmt);
134 int rc = vfprintf(out, fmt, ap);
135 fprintf(out, "\n");
136
137 va_end(ap);
138 return rc;
139 }
140
141 int
cms_context_init(cms_context * cms)142 cms_context_init(cms_context *cms)
143 {
144 memset(cms, '\0', sizeof (*cms));
145
146 cms->log = cms_common_log;
147
148 cms->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
149 if (!cms->arena)
150 cmsreterr(-1, cms, "could not create cryptographic arena");
151
152 cms->selected_digest = -1;
153
154 return 0;
155 }
156
157 void
cms_context_fini(cms_context * cms)158 cms_context_fini(cms_context *cms)
159 {
160 if (cms->cert) {
161 CERT_DestroyCertificate(cms->cert);
162 cms->cert = NULL;
163 }
164
165 if (cms->privkey) {
166 free(cms->privkey);
167 cms->privkey = NULL;
168 }
169
170 /* These were freed when the arena was destroyed */
171 if (cms->tokenname)
172 cms->tokenname = NULL;
173 if (cms->certname)
174 cms->certname = NULL;
175
176 if (cms->newsig.data) {
177 free_poison(cms->newsig.data, cms->newsig.len);
178 free(cms->newsig.data);
179 memset(&cms->newsig, '\0', sizeof (cms->newsig));
180 }
181
182 cms->selected_digest = -1;
183
184 if (cms->ci_digest) {
185 free_poison(cms->ci_digest->data, cms->ci_digest->len);
186 /* XXX sure seems like we should be freeing it here, but
187 * that's segfaulting, and we know it'll get cleaned up with
188 * PORT_FreeArena a couple of lines down.
189 */
190 cms->ci_digest = NULL;
191 }
192
193 teardown_digests(cms);
194
195 if (cms->raw_signed_attrs) {
196 free_poison(cms->raw_signed_attrs->data,
197 cms->raw_signed_attrs->len);
198 /* XXX sure seems like we should be freeing it here, but
199 * that's segfaulting, and we know it'll get cleaned up with
200 * PORT_FreeArena a couple of lines down.
201 */
202 cms->raw_signed_attrs = NULL;
203 }
204
205 if (cms->raw_signature) {
206 free_poison(cms->raw_signature->data,
207 cms->raw_signature->len);
208 /* XXX sure seems like we should be freeing it here, but
209 * that's segfaulting, and we know it'll get cleaned up with
210 * PORT_FreeArena a couple of lines down.
211 */
212 cms->raw_signature = NULL;
213 }
214
215 for (int i = 0; i < cms->num_signatures; i++) {
216 free(cms->signatures[i]->data);
217 free(cms->signatures[i]);
218 }
219
220 xfree(cms->signatures);
221 cms->num_signatures = 0;
222
223 if (cms->authbuf) {
224 xfree(cms->authbuf);
225 cms->authbuf_len = 0;
226 }
227
228 PORT_FreeArena(cms->arena, PR_TRUE);
229 memset(cms, '\0', sizeof(*cms));
230 xfree(cms);
231 }
232
233 int
cms_context_alloc(cms_context ** cmsp)234 cms_context_alloc(cms_context **cmsp)
235 {
236 cms_context *cms = calloc(1, sizeof (*cms));
237 if (!cms)
238 return -1;
239
240 int rc = cms_context_init(cms);
241 if (rc < 0) {
242 save_errno(free(cms));
243 return -1;
244 }
245 *cmsp = cms;
246 return 0;
247 }
248
cms_set_pw_callback(cms_context * cms,PK11PasswordFunc func)249 void cms_set_pw_callback(cms_context *cms, PK11PasswordFunc func)
250 {
251 cms->func = func;
252 }
253
cms_set_pw_data(cms_context * cms,void * pwdata)254 void cms_set_pw_data(cms_context *cms, void *pwdata)
255 {
256 cms->pwdata = pwdata;
257 }
258
259 int
set_digest_parameters(cms_context * cms,char * name)260 set_digest_parameters(cms_context *cms, char *name)
261 {
262 if (strcmp(name, "help")) {
263 for (int i = 0; i < n_digest_params; i++) {
264 if (!strcmp(name, digest_params[i].name)) {
265 cms->selected_digest = i;
266 return 0;
267 }
268 }
269 } else {
270 printf("Supported digests: ");
271 for (int i = 0; digest_params[i].name != NULL; i++) {
272 printf("%s ", digest_params[i].name);
273 }
274 printf("\n");
275 }
276 return -1;
277 }
278
279 struct cbdata {
280 CERTCertificate *cert;
281 PK11SlotListElement *psle;
282 secuPWData *pwdata;
283 };
284
285 static SECStatus
is_valid_cert(CERTCertificate * cert,void * data)286 is_valid_cert(CERTCertificate *cert, void *data)
287 {
288 struct cbdata *cbdata = (struct cbdata *)data;
289
290 PK11SlotInfo *slot = cbdata->psle->slot;
291 void *pwdata = cbdata->pwdata;
292
293 SECKEYPrivateKey *privkey = NULL;
294 privkey = PK11_FindPrivateKeyFromCert(slot, cert, pwdata);
295 if (privkey != NULL) {
296 cbdata->cert = cert;
297 SECKEY_DestroyPrivateKey(privkey);
298 return SECSuccess;
299 }
300 return SECFailure;
301 }
302
303 static SECStatus
is_valid_cert_without_private_key(CERTCertificate * cert,void * data)304 is_valid_cert_without_private_key(CERTCertificate *cert, void *data)
305 {
306 struct cbdata *cbdata = (struct cbdata *)data;
307 if (cert) {
308 cbdata->cert = cert;
309 return SECSuccess;
310 }
311 return SECFailure;
312 }
313
314 int
is_issuer_of(CERTCertificate * c0,CERTCertificate * c1)315 is_issuer_of(CERTCertificate *c0, CERTCertificate *c1)
316 {
317 if (c0->derSubject.len != c1->derIssuer.len)
318 return 0;
319
320 if (memcmp(c0->derSubject.data, c1->derIssuer.data, c0->derSubject.len))
321 return 0;
322 return 1;
323 }
324
325 /* This is the dumbest function ever, but we need it anyway, because nss
326 * is garbage. */
327 static void
PK11_DestroySlotListElement(PK11SlotList * slots,PK11SlotListElement ** psle)328 PK11_DestroySlotListElement(PK11SlotList *slots, PK11SlotListElement **psle)
329 {
330 while (psle && *psle)
331 *psle = PK11_GetNextSafe(slots, *psle, PR_FALSE);
332 }
333
334 int
unlock_nss_token(cms_context * cms)335 unlock_nss_token(cms_context *cms)
336 {
337 secuPWData pwdata_val = { 0, 0 };
338 void *pwdata = cms->pwdata ? cms->pwdata : &pwdata_val;
339 PK11_SetPasswordFunc(cms->func ? cms->func : SECU_GetModulePassword);
340
341 PK11SlotList *slots = NULL;
342 slots = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_TRUE, pwdata);
343 if (!slots)
344 cmsreterr(-1, cms, "could not get pk11 token list");
345
346 PK11SlotListElement *psle = NULL;
347 psle = PK11_GetFirstSafe(slots);
348 if (!psle) {
349 save_port_err(PK11_FreeSlotList(slots));
350 cmsreterr(-1, cms, "could not get pk11 safe");
351 }
352
353 while (psle) {
354 if (!strcmp(cms->tokenname, PK11_GetTokenName(psle->slot)))
355 break;
356
357 psle = PK11_GetNextSafe(slots, psle, PR_FALSE);
358 }
359
360 if (!psle) {
361 save_port_err(PK11_FreeSlotList(slots));
362 cms->log(cms, LOG_ERR, "could not find token \"%s\"",
363 cms->tokenname);
364 return -1;
365 }
366
367 SECStatus status;
368 if (PK11_NeedLogin(psle->slot) &&
369 !PK11_IsLoggedIn(psle->slot, pwdata)) {
370 status = PK11_Authenticate(psle->slot, PR_TRUE, pwdata);
371 if (status != SECSuccess) {
372 PK11_DestroySlotListElement(slots, &psle);
373 PK11_FreeSlotList(slots);
374 cms->log(cms, LOG_ERR, "authentication failed for "
375 "token \"%s\"", cms->tokenname);
376 return -1;
377 }
378 }
379
380 PK11_DestroySlotListElement(slots, &psle);
381 PK11_FreeSlotList(slots);
382 return 0;
383 }
384
385 int
find_certificate(cms_context * cms,int needs_private_key)386 find_certificate(cms_context *cms, int needs_private_key)
387 {
388 if (!cms->certname || !*cms->certname) {
389 cms->log(cms, LOG_ERR, "no certificate name specified");
390 return -1;
391 }
392
393 secuPWData pwdata_val = { 0, 0 };
394 void *pwdata = cms->pwdata ? cms->pwdata : &pwdata_val;
395 PK11_SetPasswordFunc(cms->func ? cms->func : SECU_GetModulePassword);
396
397 PK11SlotList *slots = NULL;
398 slots = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_TRUE, pwdata);
399 if (!slots)
400 cmsreterr(-1, cms, "could not get pk11 token list");
401
402 PK11SlotListElement *psle = NULL;
403 psle = PK11_GetFirstSafe(slots);
404 if (!psle) {
405 save_port_err(PK11_FreeSlotList(slots));
406 cmsreterr(-1, cms, "could not get pk11 safe");
407 }
408
409 while (psle) {
410 if (!strcmp(cms->tokenname, PK11_GetTokenName(psle->slot)))
411 break;
412
413 psle = PK11_GetNextSafe(slots, psle, PR_FALSE);
414 }
415
416 if (!psle) {
417 save_port_err(PK11_FreeSlotList(slots));
418 cms->log(cms, LOG_ERR, "could not find token \"%s\"",
419 cms->tokenname);
420 return -1;
421 }
422
423 SECStatus status;
424 if (PK11_NeedLogin(psle->slot) && !PK11_IsLoggedIn(psle->slot, pwdata)) {
425 status = PK11_Authenticate(psle->slot, PR_TRUE, pwdata);
426 if (status != SECSuccess) {
427 PK11_DestroySlotListElement(slots, &psle);
428 PK11_FreeSlotList(slots);
429 cms->log(cms, LOG_ERR, "authentication failed for "
430 "token \"%s\"", cms->tokenname);
431 return -1;
432 }
433 }
434
435 CERTCertList *certlist = NULL;
436 certlist = PK11_ListCertsInSlot(psle->slot);
437 if (!certlist) {
438 save_port_err(
439 PK11_DestroySlotListElement(slots, &psle);
440 PK11_FreeSlotList(slots));
441 cmsreterr(-1, cms, "could not get certificate list");
442 }
443
444 SECItem nickname = {
445 .data = (void *)cms->certname,
446 .len = strlen(cms->certname) + 1,
447 .type = siUTF8String,
448 };
449 struct cbdata cbdata = {
450 .cert = NULL,
451 .psle = psle,
452 .pwdata = pwdata,
453 };
454
455 if (needs_private_key) {
456 status = PK11_TraverseCertsForNicknameInSlot(&nickname,
457 psle->slot, is_valid_cert, &cbdata);
458 } else {
459 status = PK11_TraverseCertsForNicknameInSlot(&nickname,
460 psle->slot,
461 is_valid_cert_without_private_key,
462 &cbdata);
463 }
464 if (cbdata.cert == NULL) {
465 save_port_err(
466 CERT_DestroyCertList(certlist);
467 PK11_DestroySlotListElement(slots, &psle);
468 PK11_FreeSlotList(slots));
469 cmsreterr(-1, cms, "could not find certificate in list");
470 }
471
472 cms->cert = CERT_DupCertificate(cbdata.cert);
473
474 PK11_DestroySlotListElement(slots, &psle);
475 PK11_FreeSlotList(slots);
476 CERT_DestroyCertList(certlist);
477
478 return 0;
479 }
480
481 int
find_slot_for_token(cms_context * cms,PK11SlotInfo ** slot)482 find_slot_for_token(cms_context *cms, PK11SlotInfo **slot)
483 {
484 if (!cms->tokenname) {
485 cms->log(cms, LOG_ERR, "no token name specified");
486 return -1;
487 }
488
489 secuPWData pwdata_val = { 0, 0 };
490 void *pwdata = cms->pwdata ? cms->pwdata : &pwdata_val;
491 PK11_SetPasswordFunc(cms->func ? cms->func : SECU_GetModulePassword);
492
493 PK11SlotList *slots = NULL;
494 slots = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_TRUE, pwdata);
495 if (!slots)
496 cmsreterr(-1, cms, "could not get pk11 token list");
497
498 PK11SlotListElement *psle = NULL;
499 psle = PK11_GetFirstSafe(slots);
500 if (!psle) {
501 save_port_err(PK11_FreeSlotList(slots));
502 cmsreterr(-1, cms, "could not get pk11 safe");
503 }
504
505 while (psle) {
506 if (!strcmp(cms->tokenname, PK11_GetTokenName(psle->slot)))
507 break;
508
509 psle = PK11_GetNextSafe(slots, psle, PR_FALSE);
510 }
511
512 if (!psle) {
513 save_port_err(PK11_FreeSlotList(slots));
514 cms->log(cms, LOG_ERR, "could not find token \"%s\"",
515 cms->tokenname);
516 return -1;
517 }
518
519 SECStatus status;
520 if (PK11_NeedLogin(psle->slot) && !PK11_IsLoggedIn(psle->slot, pwdata)) {
521 status = PK11_Authenticate(psle->slot, PR_TRUE, pwdata);
522 if (status != SECSuccess) {
523 PK11_DestroySlotListElement(slots, &psle);
524 PK11_FreeSlotList(slots);
525 cms->log(cms, LOG_ERR, "authentication failed for "
526 "token \"%s\"", cms->tokenname);
527 return -1;
528 }
529 }
530 *slot = psle->slot;
531 return 0;
532 }
533
534 int
find_named_certificate(cms_context * cms,char * name,CERTCertificate ** cert)535 find_named_certificate(cms_context *cms, char *name, CERTCertificate **cert)
536 {
537 if (!name) {
538 cms->log(cms, LOG_ERR, "no certificate name specified");
539 return -1;
540 }
541
542 secuPWData pwdata_val = { 0, 0 };
543 void *pwdata = cms->pwdata ? cms->pwdata : &pwdata_val;
544 PK11_SetPasswordFunc(cms->func ? cms->func : SECU_GetModulePassword);
545
546 PK11SlotList *slots = NULL;
547 slots = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_TRUE, pwdata);
548 if (!slots)
549 cmsreterr(-1, cms, "could not get pk11 token list");
550
551 PK11SlotListElement *psle = NULL;
552 psle = PK11_GetFirstSafe(slots);
553 if (!psle) {
554 save_port_err(PK11_FreeSlotList(slots));
555 cmsreterr(-1, cms, "could not get pk11 safe");
556 }
557
558 while (psle) {
559 if (!strcmp(cms->tokenname, PK11_GetTokenName(psle->slot)))
560 break;
561
562 psle = PK11_GetNextSafe(slots, psle, PR_FALSE);
563 }
564
565 if (!psle) {
566 save_port_err(PK11_FreeSlotList(slots));
567 cms->log(cms, LOG_ERR, "could not find token \"%s\"",
568 cms->tokenname);
569 return -1;
570 }
571
572 SECStatus status;
573 if (PK11_NeedLogin(psle->slot) && !PK11_IsLoggedIn(psle->slot, pwdata)) {
574 status = PK11_Authenticate(psle->slot, PR_TRUE, pwdata);
575 if (status != SECSuccess) {
576 PK11_DestroySlotListElement(slots, &psle);
577 PK11_FreeSlotList(slots);
578 cms->log(cms, LOG_ERR, "authentication failed for "
579 "token \"%s\"", cms->tokenname);
580 return -1;
581 }
582 }
583
584 CERTCertList *certlist = NULL;
585 certlist = PK11_ListCertsInSlot(psle->slot);
586 if (!certlist) {
587 save_port_err(
588 PK11_DestroySlotListElement(slots, &psle);
589 PK11_FreeSlotList(slots));
590 cmsreterr(-1, cms, "could not get certificate list");
591 }
592
593 CERTCertListNode *node = NULL;
594 for (node = CERT_LIST_HEAD(certlist); !CERT_LIST_END(node,certlist);
595 node = CERT_LIST_NEXT(node)) {
596 if (!strcmp(node->cert->subjectName, name))
597 break;
598 }
599 /* If we're looking up the issuer of some cert, and the issuer isn't
600 * in the database, we'll get back what is essentially a template
601 * that's in NSS's cache waiting to be filled out. We can't use that,
602 * it'll just cause CERT_DupCertificate() to segfault. */
603 if (CERT_LIST_END(node, certlist)
604 || !node->cert || !node->cert->derCert.data
605 || !node->cert->derCert.len
606 || !node->cert->derIssuer.data
607 || !node->cert->derIssuer.len) {
608 PK11_DestroySlotListElement(slots, &psle);
609 PK11_FreeSlotList(slots);
610 CERT_DestroyCertList(certlist);
611
612 return -1;
613 }
614
615
616
617 *cert = CERT_DupCertificate(node->cert);
618
619 PK11_DestroySlotListElement(slots, &psle);
620 PK11_FreeSlotList(slots);
621 CERT_DestroyCertList(certlist);
622
623 return 0;
624 }
625
626 int
generate_string(cms_context * cms,SECItem * der,char * str)627 generate_string(cms_context *cms, SECItem *der, char *str)
628 {
629 SECItem input;
630
631 input.data = (void *)str;
632 input.len = strlen(str);
633 input.type = siBMPString;
634
635 void *ret;
636 ret = SEC_ASN1EncodeItem(cms->arena, der, &input,
637 SEC_PrintableStringTemplate);
638 if (ret == NULL)
639 cmsreterr(-1, cms, "could not encode string");
640 return 0;
641 }
642
643 static SEC_ASN1Template IntegerTemplate[] = {
644 {.kind = SEC_ASN1_INTEGER,
645 .offset = 0,
646 .sub = NULL,
647 .size = sizeof(long),
648 },
649 { 0 },
650 };
651
652 int
generate_integer(cms_context * cms,SECItem * der,unsigned long integer)653 generate_integer(cms_context *cms, SECItem *der, unsigned long integer)
654 {
655 void *ret;
656
657 uint32_t u32;
658
659 SECItem input = {
660 .data = (void *)&integer,
661 .len = sizeof(integer),
662 .type = siUnsignedInteger,
663 };
664
665 if (integer < 0x100000000) {
666 u32 = integer & 0xffffffffUL;
667 input.data = (void *)&u32;
668 input.len = sizeof(u32);
669 }
670
671 ret = SEC_ASN1EncodeItem(cms->arena, der, &input, IntegerTemplate);
672 if (ret == NULL)
673 cmsreterr(-1, cms, "could not encode data");
674 return 0;
675 }
676
677 int
generate_time(cms_context * cms,SECItem * encoded,time_t when)678 generate_time(cms_context *cms, SECItem *encoded, time_t when)
679 {
680 static char timebuf[32];
681 SECItem whenitem = {.type = SEC_ASN1_UTC_TIME,
682 .data = (unsigned char *)timebuf,
683 .len = 0
684 };
685 struct tm *tm;
686
687 tm = gmtime(&when);
688
689 whenitem.len = snprintf(timebuf, 32, "%02d%02d%02d%02d%02d%02dZ",
690 tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
691 tm->tm_hour, tm->tm_min, tm->tm_sec);
692 if (whenitem.len == 32)
693 cmsreterr(-1, cms, "could not encode timestamp");
694
695 if (SEC_ASN1EncodeItem(cms->arena, encoded, &whenitem,
696 SEC_UTCTimeTemplate) == NULL)
697 cmsreterr(-1, cms, "could not encode timestamp");
698 return 0;
699 }
700
701 static SEC_ASN1Template EmptySequenceTemplate[] = {
702 {
703 .kind = SEC_ASN1_SEQUENCE,
704 .offset = 0,
705 .sub = NULL,
706 .size = 0
707 },
708 { 0, }
709 };
710
711 int
generate_empty_sequence(cms_context * cms,SECItem * encoded)712 generate_empty_sequence(cms_context *cms, SECItem *encoded)
713 {
714 SECItem empty = {.type = SEC_ASN1_SEQUENCE,
715 .data = NULL,
716 .len = 0
717 };
718 void *ret;
719 ret = SEC_ASN1EncodeItem(cms->arena, encoded, &empty,
720 EmptySequenceTemplate);
721 if (ret == NULL)
722 cmsreterr(-1, cms, "could not encode empty sequence");
723 return 0;
724 }
725
726 static SEC_ASN1Template ContextSpecificSequence[] = {
727 {
728 .kind = SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_EXPLICIT,
729 .offset = 0,
730 .sub = &SEC_AnyTemplate,
731 .size = sizeof (SECItem),
732 },
733 { 0 }
734 };
735
736 int
make_context_specific(cms_context * cms,int ctxt,SECItem * encoded,SECItem * original)737 make_context_specific(cms_context *cms, int ctxt, SECItem *encoded,
738 SECItem *original)
739 {
740 void *rv;
741 ContextSpecificSequence[0].kind = SEC_ASN1_EXPLICIT |
742 SEC_ASN1_CONTEXT_SPECIFIC | ctxt;
743
744 rv = SEC_ASN1EncodeItem(cms->arena, encoded, original,
745 ContextSpecificSequence);
746 if (rv == NULL)
747 cmsreterr(-1, cms, "could not encode context specific data");
748 return 0;
749 }
750
751 int
generate_octet_string(cms_context * cms,SECItem * encoded,SECItem * original)752 generate_octet_string(cms_context *cms, SECItem *encoded, SECItem *original)
753 {
754 if (content_is_empty(original->data, original->len)) {
755 cms->log(cms, LOG_ERR, "content is empty, not encoding");
756 return -1;
757 }
758 if (SEC_ASN1EncodeItem(cms->arena, encoded, original,
759 SEC_OctetStringTemplate) == NULL)
760 cmsreterr(-1, cms, "could not encode octet string");
761
762 return 0;
763 }
764
765 int
generate_object_id(cms_context * cms,SECItem * der,SECOidTag tag)766 generate_object_id(cms_context *cms, SECItem *der, SECOidTag tag)
767 {
768 SECOidData *oid;
769
770 oid = SECOID_FindOIDByTag(tag);
771 if (!oid)
772 cmsreterr(-1, cms, "could not find OID");
773
774 void *ret;
775 ret = SEC_ASN1EncodeItem(cms->arena, der, &oid->oid,
776 SEC_ObjectIDTemplate);
777 if (ret == NULL)
778 cmsreterr(-1, cms, "could not encode ODI");
779 return 0;
780 }
781
782 int
generate_algorithm_id(cms_context * cms,SECAlgorithmID * idp,SECOidTag tag)783 generate_algorithm_id(cms_context *cms, SECAlgorithmID *idp, SECOidTag tag)
784 {
785 SECAlgorithmID id;
786
787 if (!idp)
788 return -1;
789
790 SECOidData *oiddata;
791 oiddata = SECOID_FindOIDByTag(tag);
792 if (!oiddata) {
793 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
794 return -1;
795 }
796 if (SECITEM_CopyItem(cms->arena, &id.algorithm, &oiddata->oid))
797 return -1;
798
799 SECITEM_AllocItem(cms->arena, &id.parameters, 2);
800 if (id.parameters.data == NULL)
801 goto err;
802 id.parameters.data[0] = SEC_ASN1_NULL;
803 id.parameters.data[1] = 0;
804 id.parameters.type = siBuffer;
805
806 memcpy(idp, &id, sizeof (id));
807 return 0;
808
809 err:
810 SECITEM_FreeItem(&id.algorithm, PR_FALSE);
811 return -1;
812 }
813
814 int
encode_algorithm_id(cms_context * cms,SECItem * der,SECOidTag tag)815 encode_algorithm_id(cms_context *cms, SECItem *der, SECOidTag tag)
816 {
817 SECAlgorithmID id;
818
819 int rc = generate_algorithm_id(cms, &id, tag);
820 if (rc < 0)
821 return rc;
822
823 void *ret;
824 ret = SEC_ASN1EncodeItem(cms->arena, der, &id,
825 SECOID_AlgorithmIDTemplate);
826 if (ret == NULL)
827 cmsreterr(-1, cms, "could not encode Algorithm ID");
828
829 return 0;
830 }
831
832 typedef struct {
833 /* L"<<<Obsolete>>>" no nul */
834 SECItem unicode;
835 } SpcString;
836
837 /* Generate DER for SpcString, which is always "<<<Obsolete>>>" in UCS-2.
838 * Irony abounds. Needs to decode like this:
839 * [0] (28)
840 * 00 3c 00 3c 00 3c 00 4f 00 62 00 73 00 6f 00
841 * 6c 00 65 00 74 00 65 00 3e 00 3e 00 3e
842 */
843 static SEC_ASN1Template SpcStringTemplate[] = {
844 {
845 .kind = SEC_ASN1_CONTEXT_SPECIFIC | 0,
846 .offset = offsetof(SpcString, unicode),
847 .sub = &SEC_BMPStringTemplate,
848 .size = sizeof (SECItem),
849 },
850 { 0, }
851 };
852
853 int
generate_spc_string(cms_context * cms,SECItem * ssp,char * str,int len)854 generate_spc_string(cms_context *cms, SECItem *ssp, char *str, int len)
855 {
856 SpcString ss;
857 memset(&ss, '\0', sizeof (ss));
858
859 SECITEM_AllocItem(cms->arena, &ss.unicode, len);
860 if (len != 0) {
861 if (!ss.unicode.data)
862 cmsreterr(-1, cms, "could not allocate memory");
863
864 memcpy(ss.unicode.data, str, len);
865 }
866 ss.unicode.type = siBMPString;
867
868 if (SEC_ASN1EncodeItem(cms->arena, ssp, &ss, SpcStringTemplate) == NULL)
869 cmsreterr(-1, cms, "could not encode SpcString");
870
871 return 0;
872 }
873
874 /* Generate the SpcLink DER. Awesomely, this needs to decode as:
875 * C-[2] (30)
876 * That is all.
877 */
878 SEC_ASN1Template SpcLinkTemplate[] = {
879 {
880 .kind = SEC_ASN1_CHOICE,
881 .offset = offsetof(SpcLink, type),
882 .sub = NULL,
883 .size = sizeof (SpcLink)
884 },
885 {
886 .kind = SEC_ASN1_CONTEXT_SPECIFIC | 0 |
887 SEC_ASN1_EXPLICIT,
888 .offset = offsetof(SpcLink, url),
889 .sub = &SEC_AnyTemplate,
890 .size = SpcLinkTypeUrl,
891 },
892 {
893 .kind = SEC_ASN1_CONSTRUCTED |
894 SEC_ASN1_CONTEXT_SPECIFIC | 2,
895 .offset = offsetof(SpcLink, file),
896 .sub = &SpcStringTemplate,
897 .size = SpcLinkTypeFile,
898 },
899 { 0, }
900 };
901
902 int
generate_spc_link(cms_context * cms,SpcLink * slp,SpcLinkType link_type,void * link_data,size_t link_data_size)903 generate_spc_link(cms_context *cms, SpcLink *slp, SpcLinkType link_type,
904 void *link_data, size_t link_data_size)
905 {
906 SpcLink sl;
907 memset(&sl, '\0', sizeof (sl));
908
909 sl.type = link_type;
910 switch (sl.type) {
911 case SpcLinkTypeFile: {
912 int rc = generate_spc_string(cms, &sl.file, link_data,
913 link_data_size);
914 if (rc < 0)
915 return rc;
916 break;
917 }
918 case SpcLinkTypeUrl:
919 sl.url.type = siBuffer;
920 sl.url.data = link_data;
921 sl.url.len = link_data_size;
922 break;
923 default:
924 cms->log(cms, LOG_ERR, "Invalid SpcLinkType");
925 return -1;
926 };
927
928 memcpy(slp, &sl, sizeof (sl));
929 return 0;
930 }
931
932 static int
check_pointer_and_size(Pe * pe,void * ptr,size_t size)933 check_pointer_and_size(Pe *pe, void *ptr, size_t size)
934 {
935 void *map = NULL;
936 size_t map_size = 0;
937
938 map = pe_rawfile(pe, &map_size);
939 if (!map || map_size < 1)
940 return 0;
941
942 if ((uintptr_t)ptr < (uintptr_t)map)
943 return 0;
944
945 if ((uintptr_t)ptr + size > (uintptr_t)map + map_size)
946 return 0;
947
948 if (ptr <= map && size >= map_size)
949 return 0;
950
951 return 1;
952 }
953
954 int
generate_digest_begin(cms_context * cms)955 generate_digest_begin(cms_context *cms)
956 {
957 struct digest *digests = NULL;
958
959 if (cms->digests) {
960 digests = cms->digests;
961 } else {
962 digests = PORT_ZAlloc(n_digest_params * sizeof (*digests));
963 if (digests == NULL)
964 cmsreterr(-1, cms, "could not allocate digest context");
965 }
966
967 for (int i = 0; i < n_digest_params; i++) {
968 digests[i].pk11ctx = PK11_CreateDigestContext(
969 digest_params[i].digest_tag);
970 if (!digests[i].pk11ctx) {
971 cms->log(cms, LOG_ERR, "%s:%s:%d could not create "
972 "digest context: %s",
973 __FILE__, __func__, __LINE__,
974 PORT_ErrorToString(PORT_GetError()));
975 goto err;
976 }
977
978 PK11_DigestBegin(digests[i].pk11ctx);
979 }
980
981 cms->digests = digests;
982 return 0;
983
984 err:
985 for (int i = 0; i < n_digest_params; i++) {
986 if (digests[i].pk11ctx)
987 PK11_DestroyContext(digests[i].pk11ctx, PR_TRUE);
988 }
989
990 free(digests);
991 return -1;
992 }
993
994 void
generate_digest_step(cms_context * cms,void * data,size_t len)995 generate_digest_step(cms_context *cms, void *data, size_t len)
996 {
997 for (int i = 0; i < n_digest_params; i++)
998 PK11_DigestOp(cms->digests[i].pk11ctx, data, len);
999 }
1000
1001 int
generate_digest_finish(cms_context * cms)1002 generate_digest_finish(cms_context *cms)
1003 {
1004 void *mark = PORT_ArenaMark(cms->arena);
1005
1006 for (int i = 0; i < n_digest_params; i++) {
1007 SECItem *digest = PORT_ArenaZAlloc(cms->arena,sizeof (SECItem));
1008 if (digest == NULL) {
1009 cms->log(cms, LOG_ERR, "%s:%s:%d could not allocate "
1010 "memory: %s", __FILE__, __func__, __LINE__,
1011 PORT_ErrorToString(PORT_GetError()));
1012 goto err;
1013 }
1014
1015 digest->type = siBuffer;
1016 digest->len = digest_params[i].size;
1017 digest->data = PORT_ArenaZAlloc(cms->arena, digest_params[i].size);
1018 if (digest->data == NULL) {
1019 cms->log(cms, LOG_ERR, "%s:%s:%d could not allocate "
1020 "memory: %s", __FILE__, __func__, __LINE__,
1021 PORT_ErrorToString(PORT_GetError()));
1022 goto err;
1023 }
1024
1025 PK11_DigestFinal(cms->digests[i].pk11ctx,
1026 digest->data, &digest->len, digest_params[i].size);
1027 PK11_Finalize(cms->digests[i].pk11ctx);
1028 PK11_DestroyContext(cms->digests[i].pk11ctx, PR_TRUE);
1029 cms->digests[i].pk11ctx = NULL;
1030 /* XXX sure seems like we should be freeing it here,
1031 * but that's segfaulting, and we know it'll get
1032 * cleaned up with PORT_FreeArena a couple of lines
1033 * down.
1034 */
1035 cms->digests[i].pe_digest = digest;
1036 }
1037
1038 PORT_ArenaUnmark(cms->arena, mark);
1039 return 0;
1040 err:
1041 for (int i = 0; i < n_digest_params; i++) {
1042 if (cms->digests[i].pk11ctx)
1043 PK11_DestroyContext(cms->digests[i].pk11ctx, PR_TRUE);
1044 }
1045 PORT_ArenaRelease(cms->arena, mark);
1046 return -1;
1047 }
1048
1049 #if 1
1050 #define dprintf(fmt, ...)
1051 #else
1052 #define dprintf(fmt, args...) printf(fmt, ## args)
1053 #endif
1054
1055 int
generate_digest(cms_context * cms,Pe * pe,int padded)1056 generate_digest(cms_context *cms, Pe *pe, int padded)
1057 {
1058 void *hash_base;
1059 size_t hash_size;
1060 struct pe32_opt_hdr *pe32opthdr = NULL;
1061 struct pe32plus_opt_hdr *pe64opthdr = NULL;
1062 unsigned long hashed_bytes = 0;
1063 int rc = -1;
1064
1065 if (!pe) {
1066 cms->log(cms, LOG_ERR, "no output pe ready");
1067 return -1;
1068 }
1069
1070 rc = generate_digest_begin(cms);
1071 if (rc < 0)
1072 return rc;
1073
1074 struct pe_hdr pehdr;
1075 if (pe_getpehdr(pe, &pehdr) == NULL)
1076 pereterr(-1, "invalid PE file header");
1077
1078 void *map = NULL;
1079 size_t map_size = 0;
1080
1081 /* 1. Load the image header into memory - should be done
1082 * 2. Initialize SHA hash context. */
1083 map = pe_rawfile(pe, &map_size);
1084 if (!map)
1085 pereterr(-1, "could not get raw output file address");
1086
1087 /* 3. Calculate the distance from the base of the image header to the
1088 * image checksum.
1089 * 4. Hash the image header from start to the beginning of the
1090 * checksum. */
1091 hash_base = map;
1092 switch (pe_kind(pe)) {
1093 case PE_K_PE_EXE: {
1094 void *opthdr = pe_getopthdr(pe);
1095 pe32opthdr = opthdr;
1096 hash_size = (uintptr_t)&pe32opthdr->csum - (uintptr_t)hash_base;
1097 break;
1098 }
1099 case PE_K_PE64_EXE: {
1100 void *opthdr = pe_getopthdr(pe);
1101 pe64opthdr = opthdr;
1102 hash_size = (uintptr_t)&pe64opthdr->csum - (uintptr_t)hash_base;
1103 break;
1104 }
1105 default:
1106 goto error;
1107 }
1108 if (!check_pointer_and_size(pe, hash_base, hash_size)) {
1109 cms->log(cms, LOG_ERR, "%s:%s:%d PE header is invalid",
1110 __FILE__, __func__, __LINE__);
1111 goto error;
1112 }
1113 dprintf("beginning of hash\n");
1114 dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);
1115 generate_digest_step(cms, hash_base, hash_size);
1116
1117 /* 5. Skip over the image checksum
1118 * 6. Get the address of the beginning of the cert dir entry
1119 * 7. Hash from the end of the csum to the start of the cert dirent. */
1120 hash_base += hash_size;
1121 hash_base += pe32opthdr ? sizeof(pe32opthdr->csum)
1122 : sizeof(pe64opthdr->csum);
1123 data_directory *dd;
1124
1125 rc = pe_getdatadir(pe, &dd);
1126 if (rc < 0 || !dd || !check_pointer_and_size(pe, dd, sizeof(*dd))) {
1127 cms->log(cms, LOG_ERR, "%s:%s:%d PE data directory is invalid",
1128 __FILE__, __func__, __LINE__);
1129 goto error;
1130 }
1131
1132 hash_size = (uintptr_t)&dd->certs - (uintptr_t)hash_base;
1133 if (!check_pointer_and_size(pe, hash_base, hash_size)) {
1134 cms->log(cms, LOG_ERR, "%s:%s:%d PE data directory is invalid",
1135 __FILE__, __func__, __LINE__);
1136 goto error;
1137 }
1138 generate_digest_step(cms, hash_base, hash_size);
1139 dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);
1140
1141 /* 8. Skip over the crt dir
1142 * 9. Hash everything up to the end of the image header. */
1143 hash_base = &dd->base_relocations;
1144 hash_size = (pe32opthdr ? pe32opthdr->header_size
1145 : pe64opthdr->header_size) -
1146 ((uintptr_t)&dd->base_relocations - (uintptr_t)map);
1147
1148 if (!check_pointer_and_size(pe, hash_base, hash_size)) {
1149 cms->log(cms, LOG_ERR, "%s:%s:%d PE relocations table is "
1150 "invalid", __FILE__, __func__, __LINE__);
1151 goto error;
1152 }
1153 generate_digest_step(cms, hash_base, hash_size);
1154 dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);
1155
1156 /* 10. Set SUM_OF_BYTES_HASHED to the size of the header. */
1157 hashed_bytes = pe32opthdr ? pe32opthdr->header_size
1158 : pe64opthdr->header_size;
1159
1160 struct section_header *shdrs = calloc(pehdr.sections, sizeof (*shdrs));
1161 if (!shdrs)
1162 goto error;
1163 Pe_Scn *scn = NULL;
1164 for (int i = 0; i < pehdr.sections; i++) {
1165 scn = pe_nextscn(pe, scn);
1166 if (scn == NULL)
1167 break;
1168 pe_getshdr(scn, &shdrs[i]);
1169 }
1170 sort_shdrs(shdrs, pehdr.sections - 1);
1171
1172 for (int i = 0; i < pehdr.sections; i++) {
1173 if (shdrs[i].raw_data_size == 0)
1174 continue;
1175
1176 hash_base = (void *)((uintptr_t)map + shdrs[i].data_addr);
1177 hash_size = shdrs[i].raw_data_size;
1178
1179 if (!check_pointer_and_size(pe, hash_base, hash_size)) {
1180 cms->log(cms, LOG_ERR, "%s:%s:%d PE section \"%s\" "
1181 "has invalid address",
1182 __FILE__, __func__, __LINE__, shdrs[i].name);
1183 goto error_shdrs;
1184 }
1185
1186 generate_digest_step(cms, hash_base, hash_size);
1187 dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);
1188
1189 hashed_bytes += hash_size;
1190 }
1191
1192 if (map_size > hashed_bytes) {
1193 hash_base = (void *)((uintptr_t)map + hashed_bytes);
1194 hash_size = map_size - dd->certs.size - hashed_bytes;
1195
1196 if (!check_pointer_and_size(pe, hash_base, hash_size)) {
1197 cms->log(cms, LOG_ERR, "%s:%s:%d PE has invalid "
1198 "trailing data", __FILE__, __func__, __LINE__);
1199 goto error_shdrs;
1200 }
1201 if (hash_size % 8 != 0 && padded) {
1202 size_t tmp_size = hash_size +
1203 ALIGNMENT_PADDING(hash_size, 8);
1204 uint8_t tmp_array[tmp_size];
1205 memset(tmp_array, '\0', tmp_size);
1206 memcpy(tmp_array, hash_base, hash_size);
1207 generate_digest_step(cms, tmp_array, tmp_size);
1208 dprintf("digesting %lx + %lx\n", (unsigned long)tmp_array, tmp_size);
1209 } else {
1210 generate_digest_step(cms, hash_base, hash_size);
1211 dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);
1212 }
1213 }
1214 dprintf("end of hash\n");
1215
1216 rc = generate_digest_finish(cms);
1217 if (rc < 0)
1218 goto error_shdrs;
1219
1220 if (shdrs) {
1221 free(shdrs);
1222 shdrs = NULL;
1223 }
1224
1225 return 0;
1226
1227 error_shdrs:
1228 if (shdrs)
1229 free(shdrs);
1230 error:
1231 return -1;
1232 }
1233
1234 /* before you run this, you'll need to enroll your CA with:
1235 * certutil -A -n 'my CA' -d /etc/pki/pesign -t CT,CT,CT -i ca.crt
1236 * And you'll need to enroll the private key like this:
1237 * pk12util -d /etc/pki/pesign/ -i Peter\ Jones.p12
1238 */
1239 int
generate_signature(cms_context * cms)1240 generate_signature(cms_context *cms)
1241 {
1242 int rc = 0;
1243
1244 if (cms->digests[cms->selected_digest].pe_digest == NULL) {
1245 cms->log(cms, LOG_ERR, "%s:%s:%d PE digest has not been "
1246 "allocated", __FILE__, __func__, __LINE__);
1247 return -1;
1248 }
1249
1250 if (content_is_empty(cms->digests[cms->selected_digest].pe_digest->data,
1251 cms->digests[cms->selected_digest].pe_digest->len)) {
1252 cms->log(cms, LOG_ERR, "%s:%s:%d PE binary has not been "
1253 "digested", __FILE__, __func__, __LINE__);
1254 return -1;
1255 }
1256
1257 SECItem sd_der;
1258 memset(&sd_der, '\0', sizeof(sd_der));
1259 rc = generate_spc_signed_data(cms, &sd_der);
1260 if (rc < 0)
1261 cmsreterr(-1, cms, "could not create signed data");
1262
1263 memcpy(&cms->newsig, &sd_der, sizeof (cms->newsig));
1264 cms->newsig.data = malloc(sd_der.len);
1265 if (!cms->newsig.data)
1266 cmsreterr(-1, cms, "could not allocate signed data");
1267 memcpy(cms->newsig.data, sd_der.data, sd_der.len);
1268 return 0;
1269 }
1270
1271 typedef struct {
1272 SECItem start;
1273 SECItem end;
1274 } Validity;
1275
1276 static SEC_ASN1Template ValidityTemplate[] = {
1277 {.kind = SEC_ASN1_SEQUENCE,
1278 .offset = 0,
1279 .sub = NULL,
1280 .size = sizeof (Validity),
1281 },
1282 {.kind = SEC_ASN1_ANY,
1283 .offset = offsetof(Validity, start),
1284 .sub = &SEC_AnyTemplate,
1285 .size = sizeof (SECItem),
1286 },
1287 {.kind = SEC_ASN1_ANY,
1288 .offset = offsetof(Validity, end),
1289 .sub = &SEC_AnyTemplate,
1290 .size = sizeof (SECItem),
1291 },
1292 { 0 }
1293 };
1294
1295 int
generate_validity(cms_context * cms,SECItem * der,time_t start,time_t end)1296 generate_validity(cms_context *cms, SECItem *der, time_t start, time_t end)
1297 {
1298 Validity validity;
1299 int rc;
1300
1301 rc = generate_time(cms, &validity.start, start);
1302 if (rc < 0)
1303 return rc;
1304
1305 rc = generate_time(cms, &validity.end, end);
1306 if (rc < 0)
1307 return rc;
1308
1309 void *ret;
1310 ret = SEC_ASN1EncodeItem(cms->arena, der, &validity, ValidityTemplate);
1311 if (ret == NULL)
1312 cmsreterr(-1, cms, "could not encode validity");
1313 return 0;
1314 }
1315
1316 static SEC_ASN1Template SetTemplate = {
1317 .kind = SEC_ASN1_SET_OF,
1318 .offset = 0,
1319 .sub = &SEC_AnyTemplate,
1320 .size = sizeof (SECItem **)
1321 };
1322
1323 int
wrap_in_set(cms_context * cms,SECItem * der,SECItem ** items)1324 wrap_in_set(cms_context *cms, SECItem *der, SECItem **items)
1325 {
1326 void *ret;
1327
1328 ret = SEC_ASN1EncodeItem(cms->arena, der, &items, &SetTemplate);
1329 if (ret == NULL)
1330 cmsreterr(-1, cms, "could not encode set");
1331 return 0;
1332 }
1333
1334 static SEC_ASN1Template SeqTemplateTemplate = {
1335 .kind = SEC_ASN1_ANY,
1336 .offset = 0,
1337 .sub = &SEC_AnyTemplate,
1338 .size = sizeof (SECItem),
1339 };
1340
1341 static SEC_ASN1Template SeqTemplateHeader = {
1342 .kind = SEC_ASN1_SEQUENCE,
1343 .offset = 0,
1344 .sub = NULL,
1345 .size = sizeof (SECItem)
1346 };
1347
1348 int
wrap_in_seq(cms_context * cms,SECItem * der,SECItem * items,int num_items)1349 wrap_in_seq(cms_context *cms, SECItem *der, SECItem *items, int num_items)
1350 {
1351 void *ret;
1352
1353 void *mark = PORT_ArenaMark(cms->arena);
1354
1355 SEC_ASN1Template tmpl[num_items+2];
1356
1357 memcpy(&tmpl[0], &SeqTemplateHeader, sizeof(*tmpl));
1358 tmpl[0].size = sizeof (SECItem) * num_items;
1359
1360 for (int i = 0; i < num_items; i++) {
1361 memcpy(&tmpl[i+1], &SeqTemplateTemplate, sizeof(SEC_ASN1Template));
1362 tmpl[i+1].offset = (i) * sizeof (SECItem);
1363 }
1364 memset(&tmpl[num_items + 1], '\0', sizeof(SEC_ASN1Template));
1365
1366 int rc = 0;
1367 ret = SEC_ASN1EncodeItem(cms->arena, der, items, tmpl);
1368 if (ret == NULL) {
1369 save_port_err(PORT_ArenaRelease(cms->arena, mark));
1370 cmsreterr(-1, cms, "could not encode set");
1371 }
1372 PORT_ArenaUnmark(cms->arena, mark);
1373 return rc;
1374 }
1375
1376 typedef struct {
1377 SECItem oid;
1378 SECItem string;
1379 } CommonName;
1380
1381 static SEC_ASN1Template CommonNameTemplate[] = {
1382 {.kind = SEC_ASN1_SEQUENCE,
1383 .offset = 0,
1384 .sub = NULL,
1385 .size = sizeof (CommonName),
1386 },
1387 {.kind = SEC_ASN1_ANY,
1388 .offset = offsetof(CommonName, oid),
1389 .sub = &SEC_AnyTemplate,
1390 .size = sizeof (SECItem),
1391 },
1392 {.kind = SEC_ASN1_ANY,
1393 .offset = offsetof(CommonName, string),
1394 .sub = &SEC_AnyTemplate,
1395 .size = sizeof (SECItem),
1396 },
1397 { 0 }
1398 };
1399
1400 int
generate_common_name(cms_context * cms,SECItem * der,char * cn_str)1401 generate_common_name(cms_context *cms, SECItem *der, char *cn_str)
1402 {
1403 CommonName cn;
1404 SECItem cn_item;
1405 int rc;
1406
1407 rc = generate_object_id(cms, &cn.oid, SEC_OID_AVA_COMMON_NAME);
1408 if (rc < 0)
1409 return rc;
1410 rc = generate_string(cms, &cn.string, cn_str);
1411 if (rc < 0)
1412 return rc;
1413
1414 void *ret;
1415 ret = SEC_ASN1EncodeItem(cms->arena, &cn_item, &cn, CommonNameTemplate);
1416 if (ret == NULL)
1417 cmsreterr(-1, cms, "could not encode common name");
1418
1419 SECItem cn_set;
1420 SECItem *items[2] = {&cn_item, NULL};
1421 rc = wrap_in_set(cms, &cn_set, items);
1422 if (rc < 0)
1423 return rc;
1424 rc = wrap_in_seq(cms, der, &cn_set, 1);
1425 if (rc < 0)
1426 return rc;
1427 return 0;
1428 }
1429
1430 typedef struct {
1431 SECItem type;
1432 SECItem value;
1433 } ava;
1434
1435 static const SEC_ASN1Template AVATemplate[] = {
1436 {.kind = SEC_ASN1_SEQUENCE,
1437 .offset = 0,
1438 .sub = NULL,
1439 .size = sizeof (ava),
1440 },
1441 {.kind = SEC_ASN1_ANY,
1442 .offset = offsetof(ava, type),
1443 .sub = &SEC_AnyTemplate,
1444 .size = sizeof (SECItem),
1445 },
1446 {.kind = SEC_ASN1_ANY,
1447 .offset = offsetof(ava, value),
1448 .sub = &SEC_AnyTemplate,
1449 .size = sizeof (SECItem),
1450 },
1451 { 0 }
1452 };
1453
1454 /* I can't figure out how to get a CERTName out in a non-rediculous form, so
1455 * we wind up encoding the whole thing manually :/ */
1456 static int
generate_ava(cms_context * cms,SECItem * der,CERTAVA * certava)1457 generate_ava(cms_context *cms, SECItem *der, CERTAVA *certava)
1458 {
1459 ava ava;
1460
1461 SECOidData *oid;
1462
1463 void *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1464 if (arena == NULL)
1465 cmsreterr(-1, cms, "could not create arena");
1466
1467 void *real_arena = cms->arena;
1468 cms->arena = arena;
1469
1470 oid = SECOID_FindOID(&certava->type);
1471 if (!oid) {
1472 save_port_err(PORT_FreeArena(arena, PR_TRUE));
1473 cms->arena = real_arena;
1474 cmsreterr(-1, cms, "could not find OID");
1475 }
1476
1477 int rc = generate_object_id(cms, &ava.type, oid->offset);
1478 if (rc < 0) {
1479 PORT_FreeArena(arena, PR_TRUE);
1480 cms->arena = real_arena;
1481 return -1;
1482 }
1483
1484 memcpy(&ava.value, &certava->value, sizeof (ava.value));
1485
1486 void *ret;
1487 SECItem tmp;
1488 ret = SEC_ASN1EncodeItem(arena, &tmp, &ava, AVATemplate);
1489 if (ret == NULL) {
1490 save_port_err(PORT_FreeArena(arena, PR_TRUE));
1491 cms->arena = real_arena;
1492 cmsreterr(-1, cms, "could not encode AVA");
1493 }
1494
1495 der->type = tmp.type;
1496 der->len = tmp.len;
1497 der->data = PORT_ArenaAlloc(real_arena, tmp.len);
1498 if (!der->data) {
1499 save_port_err(PORT_FreeArena(arena, PR_TRUE));
1500 cms->arena = real_arena;
1501 cmsreterr(-1, cms, "could not allocate AVA");
1502 }
1503 memcpy(der->data, tmp.data, tmp.len);
1504 PORT_FreeArena(arena, PR_TRUE);
1505 cms->arena = real_arena;
1506
1507 return 0;
1508 }
1509
1510 int
generate_name(cms_context * cms,SECItem * der,CERTName * certname)1511 generate_name(cms_context *cms, SECItem *der, CERTName *certname)
1512 {
1513 void *marka = PORT_ArenaMark(cms->arena);
1514 CERTRDN **rdns = certname->rdns;
1515 CERTRDN *rdn;
1516
1517 int num_items = 0;
1518 int rc = 0;
1519
1520 while (rdns && (rdn = *rdns++) != NULL) {
1521 CERTAVA **avas = rdn->avas;
1522 CERTAVA *ava;
1523 while (avas && (ava = *avas++) != NULL)
1524 num_items++;
1525 }
1526
1527 SECItem items[num_items];
1528
1529 int i = 0;
1530 rdns = certname->rdns;
1531 while (rdns && (rdn = *rdns++) != NULL) {
1532 CERTAVA **avas = rdn->avas;
1533 CERTAVA *ava;
1534 while (avas && (ava = *avas++) != NULL) {
1535 SECItem avader;
1536 rc = generate_ava(cms, &avader, ava);
1537 if (rc < 0) {
1538 PORT_ArenaRelease(cms->arena, marka);
1539 return -1;
1540 }
1541
1542 SECItem *list[2] = {
1543 &avader,
1544 NULL,
1545 };
1546 rc = wrap_in_set(cms, &items[i], list);
1547 if (rc < 0) {
1548 PORT_ArenaRelease(cms->arena, marka);
1549 return -1;
1550 }
1551 i++;
1552 }
1553 }
1554 wrap_in_seq(cms, der, &items[0], num_items);
1555 PORT_ArenaUnmark(cms->arena, marka);
1556
1557 return 0;
1558 }
1559
1560 typedef struct {
1561 SECItem oid;
1562 SECItem url;
1563 } AuthInfo;
1564
1565 static SEC_ASN1Template AuthInfoTemplate[] = {
1566 {.kind = SEC_ASN1_SEQUENCE,
1567 .offset = 0,
1568 .sub = NULL,
1569 .size = sizeof (AuthInfo),
1570 },
1571 {.kind = SEC_ASN1_OBJECT_ID,
1572 .offset = offsetof(AuthInfo, oid),
1573 .sub = &SEC_ObjectIDTemplate,
1574 .size = sizeof (SECItem),
1575 },
1576 {.kind = SEC_ASN1_ANY,
1577 .offset = offsetof(AuthInfo, url),
1578 .sub = &SEC_AnyTemplate,
1579 .size = sizeof (SECItem),
1580 },
1581 { 0 }
1582 };
1583
1584 static SEC_ASN1Template AuthInfoWrapperTemplate[] = {
1585 {.kind = SEC_ASN1_SEQUENCE,
1586 .offset = 0,
1587 .sub = NULL,
1588 .size = sizeof (AuthInfo),
1589 },
1590 {.kind = SEC_ASN1_OBJECT_ID,
1591 .offset = offsetof(AuthInfo, oid),
1592 .sub = &SEC_ObjectIDTemplate,
1593 .size = sizeof (SECItem),
1594 },
1595 {.kind = SEC_ASN1_ANY,
1596 .offset = offsetof(AuthInfo, url),
1597 .sub = &SEC_AnyTemplate,
1598 .size = sizeof (SECItem),
1599 },
1600 { 0 }
1601 };
1602
1603 int
generate_auth_info(cms_context * cms,SECItem * der,char * url)1604 generate_auth_info(cms_context *cms, SECItem *der, char *url)
1605 {
1606 AuthInfo ai;
1607
1608 SECOidData *oid = SECOID_FindOIDByTag(SEC_OID_PKIX_CA_ISSUERS);
1609 if (!oid)
1610 cmsreterr(-1, cms, "could not get CA issuers OID");
1611
1612 memcpy(&ai.oid, &oid->oid, sizeof (ai.oid));
1613
1614 SECItem urlitem = {
1615 .data = (unsigned char *)url,
1616 .len = strlen(url),
1617 .type = siBuffer
1618 };
1619 int rc = make_context_specific(cms, 6, &ai.url, &urlitem);
1620 if (rc < 0)
1621 return rc;
1622
1623 void *ret;
1624 SECItem unwrapped;
1625 ret = SEC_ASN1EncodeItem(cms->arena, &unwrapped, &ai, AuthInfoTemplate);
1626 if (ret == NULL)
1627 cmsreterr(-1, cms, "could not encode CA Issuers");
1628
1629 rc = wrap_in_seq(cms, der, &unwrapped, 1);
1630 if (rc < 0)
1631 return rc;
1632 return 0;
1633
1634 /* I've no idea how to get SEC_ASN1EncodeItem to spit out the thing
1635 * we actually want here. So once again, just force the data to
1636 * look correct :( */
1637 if (unwrapped.len < 12) {
1638 cms->log(cms, LOG_ERR, "%s:%s:%d generated CA Issuers Info "
1639 "cannot possibly be valid",
1640 __FILE__, __func__, __LINE__);
1641 return -1;
1642 }
1643 unwrapped.data[12] = 0x86;
1644 unwrapped.type = siBuffer;
1645
1646 AuthInfo wrapper;
1647 oid = SECOID_FindOIDByTag(SEC_OID_X509_AUTH_INFO_ACCESS);
1648 if (!oid)
1649 cmsreterr(-1, cms, "could not find Auth Info Access OID");
1650
1651 memcpy(&wrapper.oid, &oid->oid, sizeof (ai.oid));
1652
1653 wrap_in_seq(cms, &wrapper.url, &unwrapped, 1);
1654
1655 ret = SEC_ASN1EncodeItem(cms->arena, der, &wrapper,
1656 AuthInfoWrapperTemplate);
1657 if (ret == NULL)
1658 cmsreterr(-1, cms, "could not encode CA Issuers OID");
1659
1660 return 0;
1661 }
1662
1663 typedef struct {
1664 SECItem oid;
1665 SECItem keyhash;
1666 } KeyId;
1667
1668 int
generate_keys(cms_context * cms,PK11SlotInfo * slot,SECKEYPrivateKey ** privkey,SECKEYPublicKey ** pubkey)1669 generate_keys(cms_context *cms, PK11SlotInfo *slot,
1670 SECKEYPrivateKey **privkey, SECKEYPublicKey **pubkey)
1671 {
1672 PK11RSAGenParams rsaparams = {
1673 .keySizeInBits = 2048,
1674 .pe = 0x010001,
1675 };
1676
1677 SECStatus rv;
1678 rv = PK11_Authenticate(slot, PR_TRUE, cms->pwdata);
1679 if (rv != SECSuccess)
1680 cmsreterr(-1, cms, "could not authenticate with pk11 service");
1681
1682 void *params = &rsaparams;
1683 *privkey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN,
1684 params, pubkey, PR_TRUE, PR_TRUE,
1685 cms->pwdata);
1686 if (!*privkey)
1687 cmsreterr(-1, cms, "could not generate RSA keypair");
1688 return 0;
1689 }
1690