1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #ifdef _CRTDBG_MAP_ALLOC
6 #include <stdlib.h>
7 #include <crtdbg.h>
8 #endif
9
10 #include <nspr.h>
11 #include "secutil.h"
12 #include "pk11func.h"
13 #include "pkcs12.h"
14 #include "p12plcy.h"
15 #include "pk12util.h"
16 #include "nss.h"
17 #include "secport.h"
18 #include "secpkcs5.h"
19 #include "sechash.h"
20 #include "certdb.h"
21
22 #define PKCS12_IN_BUFFER_SIZE 200
23
24 static char *progName;
25 PRBool pk12_debugging = PR_FALSE;
26 PRBool dumpRawFile;
27 static PRBool pk12uForceUnicode;
28
29 PRIntn pk12uErrno = 0;
30
31 static void
Usage()32 Usage()
33 {
34 #define FPS PR_fprintf(PR_STDERR,
35 FPS "Usage: %s -i importfile [-d certdir] [-P dbprefix] [-h tokenname]\n",
36 progName);
37 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
38 FPS "\t\t [-v]\n");
39
40 FPS "Usage: %s -l listfile [-d certdir] [-P dbprefix] [-h tokenname]\n",
41 progName);
42 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
43 FPS "\t\t [-v]\n");
44
45 FPS "Usage: %s -o exportfile -n certname [-d certdir] [-P dbprefix]\n",
46 progName);
47 FPS "\t\t [-c key_cipher] [-C cert_cipher] [-M mac_alg]\n"
48 "\t\t [-m | --key_len keyLen] [--cert_key_len certKeyLen] [-v]\n");
49 FPS "\t\t [-k slotpwfile | -K slotpw]\n"
50 "\t\t [-w p12filepwfile | -W p12filepw]\n");
51
52 exit(PK12UERR_USAGE);
53 }
54
55 static PRBool
p12u_OpenFile(p12uContext * p12cxt,PRBool fileRead)56 p12u_OpenFile(p12uContext *p12cxt, PRBool fileRead)
57 {
58 if (!p12cxt || !p12cxt->filename) {
59 return PR_FALSE;
60 }
61
62 if (fileRead) {
63 p12cxt->file = PR_Open(p12cxt->filename,
64 PR_RDONLY, 0400);
65 } else {
66 p12cxt->file = PR_Open(p12cxt->filename,
67 PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE,
68 0600);
69 }
70
71 if (!p12cxt->file) {
72 p12cxt->error = PR_TRUE;
73 return PR_FALSE;
74 }
75
76 return PR_TRUE;
77 }
78
79 static void
p12u_DestroyContext(p12uContext ** ppCtx,PRBool removeFile)80 p12u_DestroyContext(p12uContext **ppCtx, PRBool removeFile)
81 {
82 if (!ppCtx || !(*ppCtx)) {
83 return;
84 }
85
86 if ((*ppCtx)->file != NULL) {
87 PR_Close((*ppCtx)->file);
88 }
89
90 if ((*ppCtx)->filename != NULL) {
91 if (removeFile) {
92 PR_Delete((*ppCtx)->filename);
93 }
94 PL_strfree((*ppCtx)->filename);
95 (*ppCtx)->filename = NULL;
96 }
97
98 PR_Free(*ppCtx);
99 *ppCtx = NULL;
100 }
101
102 static p12uContext *
p12u_InitContext(PRBool fileImport,char * filename)103 p12u_InitContext(PRBool fileImport, char *filename)
104 {
105 p12uContext *p12cxt;
106
107 p12cxt = PORT_ZNew(p12uContext);
108 if (!p12cxt) {
109 return NULL;
110 }
111
112 p12cxt->error = PR_FALSE;
113 p12cxt->errorValue = 0;
114 p12cxt->filename = PL_strdup(filename);
115
116 if (!p12u_OpenFile(p12cxt, fileImport)) {
117 p12u_DestroyContext(&p12cxt, PR_FALSE);
118 return NULL;
119 }
120
121 return p12cxt;
122 }
123
124 SECItem *
P12U_NicknameCollisionCallback(SECItem * old_nick,PRBool * cancel,void * wincx)125 P12U_NicknameCollisionCallback(SECItem *old_nick, PRBool *cancel, void *wincx)
126 {
127 char *nick = NULL;
128 SECItem *ret_nick = NULL;
129 CERTCertificate *cert = (CERTCertificate *)wincx;
130
131 if (!cancel || !cert) {
132 pk12uErrno = PK12UERR_USER_CANCELLED;
133 return NULL;
134 }
135
136 if (!old_nick)
137 fprintf(stdout, "pk12util: no nickname for cert in PKCS12 file.\n");
138
139 #if 0
140 /* XXX not handled yet */
141 *cancel = PR_TRUE;
142 return NULL;
143
144 #else
145
146 nick = CERT_MakeCANickname(cert);
147 if (!nick) {
148 return NULL;
149 }
150
151 if (old_nick && old_nick->data && old_nick->len &&
152 PORT_Strlen(nick) == old_nick->len &&
153 !PORT_Strncmp((char *)old_nick->data, nick, old_nick->len)) {
154 PORT_Free(nick);
155 PORT_SetError(SEC_ERROR_IO);
156 return NULL;
157 }
158
159 fprintf(stdout, "pk12util: using nickname: %s\n", nick);
160 ret_nick = PORT_ZNew(SECItem);
161 if (ret_nick == NULL) {
162 PORT_Free(nick);
163 return NULL;
164 }
165
166 ret_nick->data = (unsigned char *)nick;
167 ret_nick->len = PORT_Strlen(nick);
168
169 return ret_nick;
170 #endif
171 }
172
173 static SECStatus
p12u_SwapUnicodeBytes(SECItem * uniItem)174 p12u_SwapUnicodeBytes(SECItem *uniItem)
175 {
176 unsigned int i;
177 unsigned char a;
178 if ((uniItem == NULL) || (uniItem->len % 2)) {
179 return SECFailure;
180 }
181 for (i = 0; i < uniItem->len; i += 2) {
182 a = uniItem->data[i];
183 uniItem->data[i] = uniItem->data[i + 1];
184 uniItem->data[i + 1] = a;
185 }
186 return SECSuccess;
187 }
188
189 static PRBool
p12u_ucs2_ascii_conversion_function(PRBool toUnicode,unsigned char * inBuf,unsigned int inBufLen,unsigned char * outBuf,unsigned int maxOutBufLen,unsigned int * outBufLen,PRBool swapBytes)190 p12u_ucs2_ascii_conversion_function(PRBool toUnicode,
191 unsigned char *inBuf,
192 unsigned int inBufLen,
193 unsigned char *outBuf,
194 unsigned int maxOutBufLen,
195 unsigned int *outBufLen,
196 PRBool swapBytes)
197 {
198 SECItem it = { 0 };
199 SECItem *dup = NULL;
200 PRBool ret;
201
202 #ifdef DEBUG_CONVERSION
203 if (pk12_debugging) {
204 int i;
205 printf("Converted from:\n");
206 for (i = 0; i < inBufLen; i++) {
207 printf("%2x ", inBuf[i]);
208 /*if (i%60 == 0) printf("\n");*/
209 }
210 printf("\n");
211 }
212 #endif
213 it.data = inBuf;
214 it.len = inBufLen;
215 dup = SECITEM_DupItem(&it);
216 if (!dup) {
217 return PR_FALSE;
218 }
219 /* If converting Unicode to ASCII, swap bytes before conversion
220 * as neccessary.
221 */
222 if (!toUnicode && swapBytes) {
223 if (p12u_SwapUnicodeBytes(dup) != SECSuccess) {
224 SECITEM_ZfreeItem(dup, PR_TRUE);
225 return PR_FALSE;
226 }
227 }
228 /* Perform the conversion. */
229 ret = PORT_UCS2_UTF8Conversion(toUnicode, dup->data, dup->len,
230 outBuf, maxOutBufLen, outBufLen);
231 SECITEM_ZfreeItem(dup, PR_TRUE);
232
233 #ifdef DEBUG_CONVERSION
234 if (pk12_debugging) {
235 int i;
236 printf("Converted to:\n");
237 for (i = 0; i < *outBufLen; i++) {
238 printf("%2x ", outBuf[i]);
239 /*if (i%60 == 0) printf("\n");*/
240 }
241 printf("\n");
242 }
243 #endif
244 return ret;
245 }
246
247 SECStatus
P12U_UnicodeConversion(PLArenaPool * arena,SECItem * dest,SECItem * src,PRBool toUnicode,PRBool swapBytes)248 P12U_UnicodeConversion(PLArenaPool *arena, SECItem *dest, SECItem *src,
249 PRBool toUnicode, PRBool swapBytes)
250 {
251 unsigned int allocLen;
252 if (!dest || !src) {
253 return SECFailure;
254 }
255 allocLen = ((toUnicode) ? (src->len << 2) : src->len);
256 if (arena) {
257 dest->data = PORT_ArenaZAlloc(arena, allocLen);
258 } else {
259 dest->data = PORT_ZAlloc(allocLen);
260 }
261 if (PORT_UCS2_ASCIIConversion(toUnicode, src->data, src->len,
262 dest->data, allocLen, &dest->len,
263 swapBytes) == PR_FALSE) {
264 if (!arena) {
265 PORT_Free(dest->data);
266 }
267 dest->data = NULL;
268 return SECFailure;
269 }
270 return SECSuccess;
271 }
272
273 /*
274 *
275 */
276 SECItem *
P12U_GetP12FilePassword(PRBool confirmPw,secuPWData * p12FilePw)277 P12U_GetP12FilePassword(PRBool confirmPw, secuPWData *p12FilePw)
278 {
279 char *p0 = NULL;
280 SECItem *pwItem = NULL;
281
282 if (p12FilePw == NULL || p12FilePw->source == PW_NONE) {
283 char *p1 = NULL;
284 int rc;
285 for (;;) {
286 p0 = SECU_GetPasswordString(NULL,
287 "Enter password for PKCS12 file: ");
288 if (!confirmPw || p0 == NULL)
289 break;
290 p1 = SECU_GetPasswordString(NULL, "Re-enter password: ");
291 if (p1 == NULL) {
292 PORT_ZFree(p0, PL_strlen(p0));
293 p0 = NULL;
294 break;
295 }
296 rc = PL_strcmp(p0, p1);
297 PORT_ZFree(p1, PL_strlen(p1));
298 if (rc == 0)
299 break;
300 PORT_ZFree(p0, PL_strlen(p0));
301 }
302 } else if (p12FilePw->source == PW_FROMFILE) {
303 p0 = SECU_FilePasswd(NULL, PR_FALSE, p12FilePw->data);
304 } else { /* Plaintext */
305 p0 = PORT_Strdup(p12FilePw->data);
306 }
307
308 if (p0 == NULL) {
309 return NULL;
310 }
311 pwItem = SECITEM_AllocItem(NULL, NULL, PL_strlen(p0) + 1);
312 memcpy(pwItem->data, p0, pwItem->len);
313
314 PORT_ZFree(p0, PL_strlen(p0));
315
316 return pwItem;
317 }
318
319 SECStatus
P12U_InitSlot(PK11SlotInfo * slot,secuPWData * slotPw)320 P12U_InitSlot(PK11SlotInfo *slot, secuPWData *slotPw)
321 {
322 SECStatus rv;
323
324 /* New databases, initialize keydb password. */
325 if (PK11_NeedUserInit(slot)) {
326 rv = SECU_ChangePW(slot,
327 (slotPw->source == PW_PLAINTEXT) ? slotPw->data : 0,
328 (slotPw->source == PW_FROMFILE) ? slotPw->data : 0);
329 if (rv != SECSuccess) {
330 SECU_PrintError(progName, "Failed to initialize slot \"%s\"",
331 PK11_GetSlotName(slot));
332 return SECFailure;
333 }
334 }
335
336 if (PK11_Authenticate(slot, PR_TRUE, slotPw) != SECSuccess) {
337 SECU_PrintError(progName,
338 "Failed to authenticate to PKCS11 slot");
339 PORT_SetError(SEC_ERROR_USER_CANCELLED);
340 pk12uErrno = PK12UERR_USER_CANCELLED;
341 return SECFailure;
342 }
343
344 return SECSuccess;
345 }
346
347 /* This routine takes care of getting the PKCS12 file password, then reading and
348 * verifying the file. It returns the decoder context and a filled in password.
349 * (The password is needed by P12U_ImportPKCS12Object() to import the private
350 * key.)
351 */
352 SEC_PKCS12DecoderContext *
p12U_ReadPKCS12File(SECItem * uniPwp,char * in_file,PK11SlotInfo * slot,secuPWData * slotPw,secuPWData * p12FilePw)353 p12U_ReadPKCS12File(SECItem *uniPwp, char *in_file, PK11SlotInfo *slot,
354 secuPWData *slotPw, secuPWData *p12FilePw)
355 {
356 SEC_PKCS12DecoderContext *p12dcx = NULL;
357 p12uContext *p12cxt = NULL;
358 SECItem *pwitem = NULL;
359 SECItem p12file = { 0 };
360 SECStatus rv = SECFailure;
361 PRBool swapUnicode = PR_FALSE;
362 PRBool forceUnicode = pk12uForceUnicode;
363 PRBool trypw;
364 int error;
365
366 #ifdef IS_LITTLE_ENDIAN
367 swapUnicode = PR_TRUE;
368 #endif
369
370 p12cxt = p12u_InitContext(PR_TRUE, in_file);
371 if (!p12cxt) {
372 SECU_PrintError(progName, "File Open failed: %s", in_file);
373 pk12uErrno = PK12UERR_INIT_FILE;
374 return NULL;
375 }
376
377 /* get the password */
378 pwitem = P12U_GetP12FilePassword(PR_FALSE, p12FilePw);
379 if (!pwitem) {
380 pk12uErrno = PK12UERR_USER_CANCELLED;
381 goto done;
382 }
383
384 if (P12U_UnicodeConversion(NULL, uniPwp, pwitem, PR_TRUE,
385 swapUnicode) != SECSuccess) {
386 SECU_PrintError(progName, "Unicode conversion failed");
387 pk12uErrno = PK12UERR_UNICODECONV;
388 goto done;
389 }
390 rv = SECU_FileToItem(&p12file, p12cxt->file);
391 if (rv != SECSuccess) {
392 SECU_PrintError(progName, "Failed to read from import file");
393 goto done;
394 }
395
396 do {
397 trypw = PR_FALSE; /* normally we do this once */
398 rv = SECFailure;
399 /* init the decoder context */
400 p12dcx = SEC_PKCS12DecoderStart(uniPwp, slot, slotPw,
401 NULL, NULL, NULL, NULL, NULL);
402 if (!p12dcx) {
403 SECU_PrintError(progName, "PKCS12 decoder start failed");
404 pk12uErrno = PK12UERR_PK12DECODESTART;
405 break;
406 }
407
408 /* decode the item */
409 rv = SEC_PKCS12DecoderUpdate(p12dcx, p12file.data, p12file.len);
410
411 if (rv != SECSuccess) {
412 error = PR_GetError();
413 if (error == SEC_ERROR_DECRYPTION_DISALLOWED) {
414 PR_SetError(error, 0);
415 break;
416 }
417 SECU_PrintError(progName, "PKCS12 decoding failed");
418 pk12uErrno = PK12UERR_DECODE;
419 }
420
421 /* does the blob authenticate properly? */
422 rv = SEC_PKCS12DecoderVerify(p12dcx);
423 if (rv != SECSuccess) {
424 if (uniPwp->len == 2) {
425 /* this is a null PW, try once more with a zero-length PW
426 instead of a null string */
427 SEC_PKCS12DecoderFinish(p12dcx);
428 uniPwp->len = 0;
429 trypw = PR_TRUE;
430 } else if (forceUnicode == pk12uForceUnicode) {
431 /* try again with a different password encoding */
432 forceUnicode = !pk12uForceUnicode;
433 rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE,
434 forceUnicode);
435 if (rv != SECSuccess) {
436 SECU_PrintError(progName, "PKCS12 decoding failed to set option");
437 pk12uErrno = PK12UERR_DECODEVERIFY;
438 break;
439 }
440 SEC_PKCS12DecoderFinish(p12dcx);
441 trypw = PR_TRUE;
442 } else {
443 SECU_PrintError(progName, "PKCS12 decode not verified");
444 pk12uErrno = PK12UERR_DECODEVERIFY;
445 break;
446 }
447 }
448 } while (trypw == PR_TRUE);
449
450 /* revert the option setting */
451 if (forceUnicode != pk12uForceUnicode) {
452 rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, pk12uForceUnicode);
453 if (rv != SECSuccess) {
454 SECU_PrintError(progName, "PKCS12 decoding failed to set option");
455 pk12uErrno = PK12UERR_DECODEVERIFY;
456 }
457 }
458 /* rv has been set at this point */
459
460 done:
461 if (rv != SECSuccess) {
462 if (p12dcx != NULL) {
463 SEC_PKCS12DecoderFinish(p12dcx);
464 p12dcx = NULL;
465 }
466 if (uniPwp->data) {
467 SECITEM_ZfreeItem(uniPwp, PR_FALSE);
468 uniPwp->data = NULL;
469 }
470 }
471 PR_Close(p12cxt->file);
472 p12cxt->file = NULL;
473 /* PK11_FreeSlot(slot); */
474 p12u_DestroyContext(&p12cxt, PR_FALSE);
475
476 if (pwitem) {
477 SECITEM_ZfreeItem(pwitem, PR_TRUE);
478 }
479 SECITEM_ZfreeItem(&p12file, PR_FALSE);
480 return p12dcx;
481 }
482
483 /*
484 * given a filename for pkcs12 file, imports certs and keys
485 *
486 * Change: altitude
487 * I've changed this function so that it takes the keydb and pkcs12 file
488 * passwords from files. The "pwdKeyDB" and "pwdP12File"
489 * variables have been added for this purpose.
490 */
491 PRIntn
P12U_ImportPKCS12Object(char * in_file,PK11SlotInfo * slot,secuPWData * slotPw,secuPWData * p12FilePw)492 P12U_ImportPKCS12Object(char *in_file, PK11SlotInfo *slot,
493 secuPWData *slotPw, secuPWData *p12FilePw)
494 {
495 SEC_PKCS12DecoderContext *p12dcx = NULL;
496 SECItem uniPwitem = { 0 };
497 PRBool forceUnicode = pk12uForceUnicode;
498 PRBool trypw;
499 SECStatus rv = SECFailure;
500
501 rv = P12U_InitSlot(slot, slotPw);
502 if (rv != SECSuccess) {
503 SECU_PrintError(progName, "Failed to authenticate to \"%s\"",
504 PK11_GetSlotName(slot));
505 pk12uErrno = PK12UERR_PK11GETSLOT;
506 return rv;
507 }
508
509 do {
510 trypw = PR_FALSE; /* normally we do this once */
511 rv = SECFailure;
512 p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, p12FilePw);
513
514 if (p12dcx == NULL) {
515 goto loser;
516 }
517
518 /* make sure the bags are okey dokey -- nicknames correct, etc. */
519 rv = SEC_PKCS12DecoderValidateBags(p12dcx, P12U_NicknameCollisionCallback);
520 if (rv != SECSuccess) {
521 if (PORT_GetError() == SEC_ERROR_PKCS12_DUPLICATE_DATA) {
522 pk12uErrno = PK12UERR_CERTALREADYEXISTS;
523 } else {
524 pk12uErrno = PK12UERR_DECODEVALIBAGS;
525 }
526 SECU_PrintError(progName, "PKCS12 decode validate bags failed");
527 goto loser;
528 }
529
530 /* stuff 'em in */
531 if (forceUnicode != pk12uForceUnicode) {
532 rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE,
533 forceUnicode);
534 if (rv != SECSuccess) {
535 SECU_PrintError(progName, "PKCS12 decode set option failed");
536 pk12uErrno = PK12UERR_DECODEIMPTBAGS;
537 goto loser;
538 }
539 }
540 rv = SEC_PKCS12DecoderImportBags(p12dcx);
541 if (rv != SECSuccess) {
542 if (PR_GetError() == SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY &&
543 forceUnicode == pk12uForceUnicode) {
544 /* try again with a different password encoding */
545 forceUnicode = !pk12uForceUnicode;
546 SEC_PKCS12DecoderFinish(p12dcx);
547 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
548 trypw = PR_TRUE;
549 } else {
550 SECU_PrintError(progName, "PKCS12 decode import bags failed");
551 pk12uErrno = PK12UERR_DECODEIMPTBAGS;
552 goto loser;
553 }
554 }
555 } while (trypw);
556
557 /* revert the option setting */
558 if (forceUnicode != pk12uForceUnicode) {
559 rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, pk12uForceUnicode);
560 if (rv != SECSuccess) {
561 SECU_PrintError(progName, "PKCS12 decode set option failed");
562 pk12uErrno = PK12UERR_DECODEIMPTBAGS;
563 goto loser;
564 }
565 }
566
567 fprintf(stdout, "%s: PKCS12 IMPORT SUCCESSFUL\n", progName);
568 rv = SECSuccess;
569
570 loser:
571 if (p12dcx) {
572 SEC_PKCS12DecoderFinish(p12dcx);
573 }
574
575 if (uniPwitem.data) {
576 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
577 }
578
579 return rv;
580 }
581
582 static void
p12u_DoPKCS12ExportErrors()583 p12u_DoPKCS12ExportErrors()
584 {
585 PRErrorCode error_value;
586
587 error_value = PORT_GetError();
588 if ((error_value == SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY) ||
589 (error_value == SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME) ||
590 (error_value == SEC_ERROR_PKCS12_UNABLE_TO_WRITE)) {
591 fputs(SECU_Strerror(error_value), stderr);
592 } else if (error_value == SEC_ERROR_USER_CANCELLED) {
593 ;
594 } else {
595 fputs(SECU_Strerror(SEC_ERROR_EXPORTING_CERTIFICATES), stderr);
596 }
597 }
598
599 static void
p12u_WriteToExportFile(void * arg,const char * buf,unsigned long len)600 p12u_WriteToExportFile(void *arg, const char *buf, unsigned long len)
601 {
602 p12uContext *p12cxt = arg;
603 int writeLen;
604
605 if (!p12cxt || (p12cxt->error == PR_TRUE)) {
606 return;
607 }
608
609 if (p12cxt->file == NULL) {
610 p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
611 p12cxt->error = PR_TRUE;
612 return;
613 }
614
615 writeLen = PR_Write(p12cxt->file, (unsigned char *)buf, (PRInt32)len);
616
617 if (writeLen != (int)len) {
618 PR_Close(p12cxt->file);
619 PL_strfree(p12cxt->filename);
620 p12cxt->filename = NULL;
621 p12cxt->file = NULL;
622 p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
623 p12cxt->error = PR_TRUE;
624 }
625 }
626
627 void
P12U_ExportPKCS12Object(char * nn,char * outfile,PK11SlotInfo * inSlot,SECOidTag cipher,SECOidTag certCipher,SECOidTag hash,secuPWData * slotPw,secuPWData * p12FilePw)628 P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot,
629 SECOidTag cipher, SECOidTag certCipher, SECOidTag hash,
630 secuPWData *slotPw, secuPWData *p12FilePw)
631 {
632 SEC_PKCS12ExportContext *p12ecx = NULL;
633 SEC_PKCS12SafeInfo *keySafe = NULL, *certSafe = NULL;
634 SECItem *pwitem = NULL;
635 p12uContext *p12cxt = NULL;
636 CERTCertList *certlist = NULL;
637 CERTCertListNode *node = NULL;
638 PK11SlotInfo *slot = NULL;
639
640 if (P12U_InitSlot(inSlot, slotPw) != SECSuccess) {
641 SECU_PrintError(progName, "Failed to authenticate to \"%s\"",
642 PK11_GetSlotName(inSlot));
643 pk12uErrno = PK12UERR_PK11GETSLOT;
644 goto loser;
645 }
646 certlist = PK11_FindCertsFromNickname(nn, slotPw);
647 if (!certlist) {
648 PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
649 SECU_PrintError(progName, "find user certs from nickname failed");
650 pk12uErrno = PK12UERR_FINDCERTBYNN;
651 return;
652 }
653
654 if ((SECSuccess != CERT_FilterCertListForUserCerts(certlist)) ||
655 CERT_LIST_EMPTY(certlist)) {
656 PR_fprintf(PR_STDERR, "%s: no user certs from given nickname\n",
657 progName);
658 pk12uErrno = PK12UERR_FINDCERTBYNN;
659 goto loser;
660 }
661
662 /* Password to use for PKCS12 file. */
663 pwitem = P12U_GetP12FilePassword(PR_TRUE, p12FilePw);
664 if (!pwitem) {
665 goto loser;
666 }
667
668 p12cxt = p12u_InitContext(PR_FALSE, outfile);
669 if (!p12cxt) {
670 SECU_PrintError(progName, "Initialization failed: %s", outfile);
671 pk12uErrno = PK12UERR_INIT_FILE;
672 goto loser;
673 }
674
675 if (certlist) {
676 CERTCertificate *cert = CERT_LIST_HEAD(certlist)->cert;
677 if (cert) {
678 slot = cert->slot; /* use the slot from the first matching
679 certificate to create the context . This is for keygen */
680 }
681 }
682 if (!slot) {
683 SECU_PrintError(progName, "cert does not have a slot");
684 pk12uErrno = PK12UERR_FINDCERTBYNN;
685 goto loser;
686 }
687 p12ecx = SEC_PKCS12CreateExportContext(NULL, NULL, slot, slotPw);
688 if (!p12ecx) {
689 SECU_PrintError(progName, "export context creation failed");
690 pk12uErrno = PK12UERR_EXPORTCXCREATE;
691 goto loser;
692 }
693
694 if (SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, hash) !=
695 SECSuccess) {
696 SECU_PrintError(progName, "PKCS12 add password integrity failed");
697 pk12uErrno = PK12UERR_PK12ADDPWDINTEG;
698 goto loser;
699 }
700
701 for (node = CERT_LIST_HEAD(certlist);
702 !CERT_LIST_END(node, certlist);
703 node = CERT_LIST_NEXT(node)) {
704 CERTCertificate *cert = node->cert;
705 if (!cert->slot) {
706 SECU_PrintError(progName, "cert does not have a slot");
707 pk12uErrno = PK12UERR_FINDCERTBYNN;
708 goto loser;
709 }
710
711 keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
712 if (certCipher == SEC_OID_UNKNOWN) {
713 certSafe = keySafe;
714 } else {
715 certSafe =
716 SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certCipher);
717 }
718
719 if (!certSafe || !keySafe) {
720 SECU_PrintError(progName, "key or cert safe creation failed");
721 pk12uErrno = PK12UERR_CERTKEYSAFE;
722 goto loser;
723 }
724
725 if (SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert,
726 CERT_GetDefaultCertDB(), keySafe, NULL,
727 PR_TRUE, pwitem, cipher) != SECSuccess) {
728 SECU_PrintError(progName, "add cert and key failed");
729 pk12uErrno = PK12UERR_ADDCERTKEY;
730 goto loser;
731 }
732 }
733
734 CERT_DestroyCertList(certlist);
735 certlist = NULL;
736
737 if (SEC_PKCS12Encode(p12ecx, p12u_WriteToExportFile, p12cxt) !=
738 SECSuccess) {
739 SECU_PrintError(progName, "PKCS12 encode failed");
740 pk12uErrno = PK12UERR_ENCODE;
741 goto loser;
742 }
743
744 p12u_DestroyContext(&p12cxt, PR_FALSE);
745 SECITEM_ZfreeItem(pwitem, PR_TRUE);
746 fprintf(stdout, "%s: PKCS12 EXPORT SUCCESSFUL\n", progName);
747 SEC_PKCS12DestroyExportContext(p12ecx);
748
749 return;
750
751 loser:
752 SEC_PKCS12DestroyExportContext(p12ecx);
753
754 if (certlist) {
755 CERT_DestroyCertList(certlist);
756 certlist = NULL;
757 }
758
759 p12u_DestroyContext(&p12cxt, PR_TRUE);
760 if (pwitem) {
761 SECITEM_ZfreeItem(pwitem, PR_TRUE);
762 }
763 p12u_DoPKCS12ExportErrors();
764 return;
765 }
766
767 PRIntn
P12U_ListPKCS12File(char * in_file,PK11SlotInfo * slot,secuPWData * slotPw,secuPWData * p12FilePw)768 P12U_ListPKCS12File(char *in_file, PK11SlotInfo *slot,
769 secuPWData *slotPw, secuPWData *p12FilePw)
770 {
771 SEC_PKCS12DecoderContext *p12dcx = NULL;
772 SECItem uniPwitem = { 0 };
773 SECStatus rv = SECFailure;
774 const SEC_PKCS12DecoderItem *dip;
775
776 p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, p12FilePw);
777 /* did the blob authenticate properly? */
778 if (p12dcx == NULL) {
779 SECU_PrintError(progName, "PKCS12 decode not verified");
780 pk12uErrno = PK12UERR_DECODEVERIFY;
781 goto loser;
782 }
783 rv = SEC_PKCS12DecoderIterateInit(p12dcx);
784 if (rv != SECSuccess) {
785 SECU_PrintError(progName, "PKCS12 decode iterate bags failed");
786 pk12uErrno = PK12UERR_DECODEIMPTBAGS;
787 rv = SECFailure;
788 } else {
789 int fileCounter = 0;
790 while (SEC_PKCS12DecoderIterateNext(p12dcx, &dip) == SECSuccess) {
791 switch (dip->type) {
792 case SEC_OID_PKCS12_V1_CERT_BAG_ID:
793 printf("Certificate");
794 if (dumpRawFile) {
795 PRFileDesc *fd;
796 char fileName[20];
797 sprintf(fileName, "file%04d.der", ++fileCounter);
798 fd = PR_Open(fileName,
799 PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE,
800 0600);
801 if (!fd) {
802 SECU_PrintError(progName,
803 "Cannot create output file");
804 } else {
805 PR_Write(fd, dip->der->data, dip->der->len);
806 PR_Close(fd);
807 }
808 } else if (SECU_PrintSignedData(stdout, dip->der,
809 (dip->hasKey) ? "(has private key)"
810 : "",
811 0, (SECU_PPFunc)SECU_PrintCertificate) !=
812 0) {
813 SECU_PrintError(progName, "PKCS12 print cert bag failed");
814 }
815 if (dip->friendlyName != NULL) {
816 printf(" Friendly Name: %s\n\n",
817 dip->friendlyName->data);
818 }
819 if (dip->shroudAlg) {
820 SECU_PrintAlgorithmID(stdout, dip->shroudAlg,
821 "Encryption algorithm", 1);
822 }
823 break;
824 case SEC_OID_PKCS12_V1_KEY_BAG_ID:
825 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
826 printf("Key");
827 if (dip->type == SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID)
828 printf("(shrouded)");
829 printf(":\n");
830 if (dip->friendlyName != NULL) {
831 printf(" Friendly Name: %s\n\n",
832 dip->friendlyName->data);
833 }
834 if (dip->shroudAlg) {
835 SECU_PrintAlgorithmID(stdout, dip->shroudAlg,
836 "Encryption algorithm", 1);
837 }
838 break;
839 default:
840 printf("unknown bag type(%d): %s\n\n", dip->type,
841 SECOID_FindOIDTagDescription(dip->type));
842 break;
843 }
844 }
845 rv = SECSuccess;
846 }
847
848 loser:
849
850 if (p12dcx) {
851 SEC_PKCS12DecoderFinish(p12dcx);
852 }
853
854 if (uniPwitem.data) {
855 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
856 }
857
858 return rv;
859 }
860
861 SECOidTag
PKCS12U_FindTagFromString(char * cipherString)862 PKCS12U_FindTagFromString(char *cipherString)
863 {
864 SECOidTag tag;
865 SECOidData *oid;
866
867 /* future enhancement: accept dotted oid spec? */
868
869 for (tag = 1; (oid = SECOID_FindOIDByTag(tag)) != NULL; tag++) {
870 /* only interested in oids that we actually understand */
871 if (oid->mechanism == CKM_INVALID_MECHANISM) {
872 continue;
873 }
874 if (PORT_Strcasecmp(oid->desc, cipherString) != 0) {
875 continue;
876 }
877 return tag;
878 }
879 return SEC_OID_UNKNOWN;
880 }
881
882 /*
883 * use the oid table description to map a user input string to a particular
884 * oid.
885 */
886 SECOidTag
PKCS12U_MapCipherFromString(char * cipherString,int keyLen)887 PKCS12U_MapCipherFromString(char *cipherString, int keyLen)
888 {
889 SECOidTag tag;
890 SECOidTag cipher;
891
892 /* future enhancement: provide 'friendlier' typed in names for
893 * pbe mechanisms.
894 */
895
896 /* look for the oid tag by Description */
897 tag = PKCS12U_FindTagFromString(cipherString);
898 if (tag == SEC_OID_UNKNOWN) {
899 return tag;
900 }
901
902 cipher = SEC_OID_UNKNOWN;
903 /* we found a match... get the PBE version of this
904 * cipher... */
905 if (!SEC_PKCS5IsAlgorithmPBEAlgTag(tag)) {
906 cipher = SEC_PKCS5GetPBEAlgorithm(tag, keyLen);
907 /* no eqivalent PKCS5/PKCS12 cipher, use the raw
908 * encryption tag we got and pass it directly in,
909 * pkcs12 will use the pkcsv5 mechanism */
910 if (cipher == SEC_OID_PKCS5_PBES2) {
911 cipher = tag;
912 } else if (cipher == SEC_OID_PKCS5_PBMAC1) {
913 /* make sure we have not macing ciphers here */
914 cipher = SEC_OID_UNKNOWN;
915 }
916 } else {
917 cipher = tag;
918 }
919 return cipher;
920 }
921
922 SECOidTag
PKCS12U_MapHashFromString(char * hashString)923 PKCS12U_MapHashFromString(char *hashString)
924 {
925 SECOidTag hashAlg;
926
927 /* look for the oid tag by Description */
928 hashAlg = PKCS12U_FindTagFromString(hashString);
929 if (hashAlg == SEC_OID_UNKNOWN) {
930 return hashAlg;
931 }
932 /* make sure it's a hashing oid */
933 if (HASH_GetHashTypeByOidTag(hashAlg) == HASH_AlgNULL) {
934 return SEC_OID_UNKNOWN;
935 }
936 return hashAlg;
937 }
938
939 static void
p12u_EnableAllCiphers()940 p12u_EnableAllCiphers()
941 {
942 SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
943 SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
944 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
945 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
946 SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
947 SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
948 SEC_PKCS12EnableCipher(PKCS12_AES_CBC_128, 1);
949 SEC_PKCS12EnableCipher(PKCS12_AES_CBC_192, 1);
950 SEC_PKCS12EnableCipher(PKCS12_AES_CBC_256, 1);
951 SEC_PKCS12SetPreferredCipher(PKCS12_AES_CBC_256, 1);
952 }
953
954 static PRUintn
P12U_Init(char * dir,char * dbprefix,PRBool listonly)955 P12U_Init(char *dir, char *dbprefix, PRBool listonly)
956 {
957 SECStatus rv;
958 PK11_SetPasswordFunc(SECU_GetModulePassword);
959
960 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
961 if (listonly && NSS_NoDB_Init("") == SECSuccess) {
962 rv = SECSuccess;
963 } else {
964 rv = NSS_Initialize(dir, dbprefix, dbprefix, "secmod.db", 0);
965 }
966 if (rv != SECSuccess) {
967 SECU_PrintPRandOSError(progName);
968 exit(-1);
969 }
970
971 /* setup unicode callback functions */
972 PORT_SetUCS2_ASCIIConversionFunction(p12u_ucs2_ascii_conversion_function);
973 /* use the defaults for UCS4-UTF8 and UCS2-UTF8 */
974
975 p12u_EnableAllCiphers();
976
977 return 0;
978 }
979
980 enum {
981 opt_CertDir = 0,
982 opt_TokenName,
983 opt_Import,
984 opt_SlotPWFile,
985 opt_SlotPW,
986 opt_List,
987 opt_Nickname,
988 opt_Export,
989 opt_Raw,
990 opt_P12FilePWFile,
991 opt_P12FilePW,
992 opt_DBPrefix,
993 opt_Debug,
994 opt_Cipher,
995 opt_CertCipher,
996 opt_KeyLength,
997 opt_CertKeyLength,
998 opt_Mac
999 };
1000
1001 static secuCommandFlag pk12util_options[] =
1002 {
1003 { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE },
1004 { /* opt_TokenName */ 'h', PR_TRUE, 0, PR_FALSE },
1005 { /* opt_Import */ 'i', PR_TRUE, 0, PR_FALSE },
1006 { /* opt_SlotPWFile */ 'k', PR_TRUE, 0, PR_FALSE },
1007 { /* opt_SlotPW */ 'K', PR_TRUE, 0, PR_FALSE },
1008 { /* opt_List */ 'l', PR_TRUE, 0, PR_FALSE },
1009 { /* opt_Nickname */ 'n', PR_TRUE, 0, PR_FALSE },
1010 { /* opt_Export */ 'o', PR_TRUE, 0, PR_FALSE },
1011 { /* opt_Raw */ 'r', PR_FALSE, 0, PR_FALSE },
1012 { /* opt_P12FilePWFile */ 'w', PR_TRUE, 0, PR_FALSE },
1013 { /* opt_P12FilePW */ 'W', PR_TRUE, 0, PR_FALSE },
1014 { /* opt_DBPrefix */ 'P', PR_TRUE, 0, PR_FALSE },
1015 { /* opt_Debug */ 'v', PR_FALSE, 0, PR_FALSE },
1016 { /* opt_Cipher */ 'c', PR_TRUE, 0, PR_FALSE },
1017 { /* opt_CertCipher */ 'C', PR_TRUE, 0, PR_FALSE },
1018 { /* opt_KeyLength */ 'm', PR_TRUE, 0, PR_FALSE, "key_len" },
1019 { /* opt_CertKeyLength */ 0, PR_TRUE, 0, PR_FALSE, "cert_key_len" },
1020 { /* opt_Mac */ 'M', PR_TRUE, 0, PR_FALSE, PR_FALSE }
1021 };
1022
1023 int
main(int argc,char ** argv)1024 main(int argc, char **argv)
1025 {
1026 secuPWData slotPw = { PW_NONE, NULL };
1027 secuPWData p12FilePw = { PW_NONE, NULL };
1028 PK11SlotInfo *slot;
1029 char *slotname = NULL;
1030 char *import_file = NULL;
1031 char *export_file = NULL;
1032 char *dbprefix = "";
1033 SECStatus rv;
1034 SECOidTag cipher = SEC_OID_AES_256_CBC;
1035 SECOidTag hash = SEC_OID_SHA256;
1036 SECOidTag certCipher = SEC_OID_AES_128_CBC;
1037 int keyLen = 0;
1038 int certKeyLen = 0;
1039 secuCommand pk12util;
1040 PRInt32 forceUnicode;
1041
1042 #ifdef _CRTDBG_MAP_ALLOC
1043 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
1044 #endif
1045
1046 pk12util.numCommands = 0;
1047 pk12util.commands = 0;
1048 pk12util.numOptions = sizeof(pk12util_options) / sizeof(secuCommandFlag);
1049 pk12util.options = pk12util_options;
1050
1051 progName = strrchr(argv[0], '/');
1052 progName = progName ? progName + 1 : argv[0];
1053
1054 rv = SECU_ParseCommandLine(argc, argv, progName, &pk12util);
1055
1056 if (rv != SECSuccess)
1057 Usage();
1058
1059 pk12_debugging = pk12util.options[opt_Debug].activated;
1060
1061 if ((pk12util.options[opt_Import].activated +
1062 pk12util.options[opt_Export].activated +
1063 pk12util.options[opt_List].activated) != 1) {
1064 Usage();
1065 }
1066
1067 if (pk12util.options[opt_Export].activated &&
1068 !pk12util.options[opt_Nickname].activated) {
1069 Usage();
1070 }
1071
1072 rv = NSS_OptionGet(__NSS_PKCS12_DECODE_FORCE_UNICODE, &forceUnicode);
1073 if (rv != SECSuccess) {
1074 SECU_PrintError(progName,
1075 "Failed to get NSS_PKCS12_DECODE_FORCE_UNICODE option");
1076 Usage();
1077 }
1078 pk12uForceUnicode = forceUnicode;
1079
1080 slotname = SECU_GetOptionArg(&pk12util, opt_TokenName);
1081
1082 import_file = (pk12util.options[opt_List].activated) ? SECU_GetOptionArg(&pk12util, opt_List)
1083 : SECU_GetOptionArg(&pk12util, opt_Import);
1084 export_file = SECU_GetOptionArg(&pk12util, opt_Export);
1085
1086 if (pk12util.options[opt_P12FilePWFile].activated) {
1087 p12FilePw.source = PW_FROMFILE;
1088 p12FilePw.data = PORT_Strdup(pk12util.options[opt_P12FilePWFile].arg);
1089 }
1090
1091 if (pk12util.options[opt_P12FilePW].activated) {
1092 p12FilePw.source = PW_PLAINTEXT;
1093 p12FilePw.data = PORT_Strdup(pk12util.options[opt_P12FilePW].arg);
1094 }
1095
1096 if (pk12util.options[opt_SlotPWFile].activated) {
1097 slotPw.source = PW_FROMFILE;
1098 slotPw.data = PORT_Strdup(pk12util.options[opt_SlotPWFile].arg);
1099 }
1100
1101 if (pk12util.options[opt_SlotPW].activated) {
1102 slotPw.source = PW_PLAINTEXT;
1103 slotPw.data = PORT_Strdup(pk12util.options[opt_SlotPW].arg);
1104 }
1105
1106 if (pk12util.options[opt_CertDir].activated) {
1107 SECU_ConfigDirectory(pk12util.options[opt_CertDir].arg);
1108 }
1109 if (pk12util.options[opt_DBPrefix].activated) {
1110 dbprefix = pk12util.options[opt_DBPrefix].arg;
1111 }
1112 if (pk12util.options[opt_Raw].activated) {
1113 dumpRawFile = PR_TRUE;
1114 }
1115 if (pk12util.options[opt_KeyLength].activated) {
1116 keyLen = atoi(pk12util.options[opt_KeyLength].arg);
1117 }
1118 if (pk12util.options[opt_CertKeyLength].activated) {
1119 certKeyLen = atoi(pk12util.options[opt_CertKeyLength].arg);
1120 }
1121
1122 P12U_Init(SECU_ConfigDirectory(NULL), dbprefix,
1123 pk12util.options[opt_List].activated);
1124
1125 if (!slotname || PL_strcmp(slotname, "internal") == 0)
1126 slot = PK11_GetInternalKeySlot();
1127 else
1128 slot = PK11_FindSlotByName(slotname);
1129
1130 if (!slot) {
1131 SECU_PrintError(progName, "Invalid slot \"%s\"", slotname);
1132 pk12uErrno = PK12UERR_PK11GETSLOT;
1133 goto done;
1134 }
1135
1136 if (pk12util.options[opt_Cipher].activated) {
1137 char *cipherString = pk12util.options[opt_Cipher].arg;
1138
1139 cipher = PKCS12U_MapCipherFromString(cipherString, keyLen);
1140 /* We only want encryption PBE's. make sure we don't have
1141 * any MAC pbes */
1142 if (cipher == SEC_OID_UNKNOWN) {
1143 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
1144 SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString);
1145 pk12uErrno = PK12UERR_INVALIDALGORITHM;
1146 goto done;
1147 }
1148 }
1149
1150 if (pk12util.options[opt_CertCipher].activated) {
1151 char *cipherString = pk12util.options[opt_CertCipher].arg;
1152
1153 if (PORT_Strcasecmp(cipherString, "none") == 0) {
1154 certCipher = SEC_OID_UNKNOWN;
1155 } else {
1156 certCipher = PKCS12U_MapCipherFromString(cipherString, certKeyLen);
1157 /* If the user requested a cipher and we didn't find it, then
1158 * don't just silently not encrypt. */
1159 if (certCipher == SEC_OID_UNKNOWN) {
1160 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
1161 SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString);
1162 pk12uErrno = PK12UERR_INVALIDALGORITHM;
1163 goto done;
1164 }
1165 }
1166 }
1167 if (pk12util.options[opt_Mac].activated) {
1168 char *hashString = pk12util.options[opt_Mac].arg;
1169
1170 hash = PKCS12U_MapHashFromString(hashString);
1171 /* We don't support creating Mac-less pkcs 12 files */
1172 if (hash == SEC_OID_UNKNOWN) {
1173 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
1174 SECU_PrintError(progName, "Algorithm: \"%s\"", hashString);
1175 pk12uErrno = PK12UERR_INVALIDALGORITHM;
1176 goto done;
1177 }
1178 }
1179
1180 if (pk12util.options[opt_Import].activated) {
1181 P12U_ImportPKCS12Object(import_file, slot, &slotPw, &p12FilePw);
1182
1183 } else if (pk12util.options[opt_Export].activated) {
1184 P12U_ExportPKCS12Object(pk12util.options[opt_Nickname].arg,
1185 export_file, slot, cipher, certCipher,
1186 hash, &slotPw, &p12FilePw);
1187
1188 } else if (pk12util.options[opt_List].activated) {
1189 P12U_ListPKCS12File(import_file, slot, &slotPw, &p12FilePw);
1190
1191 } else {
1192 Usage();
1193 pk12uErrno = PK12UERR_USAGE;
1194 }
1195
1196 done:
1197 if (import_file != NULL)
1198 PORT_ZFree(import_file, PL_strlen(import_file));
1199 if (export_file != NULL)
1200 PORT_ZFree(export_file, PL_strlen(export_file));
1201 if (slotPw.data != NULL)
1202 PORT_ZFree(slotPw.data, PL_strlen(slotPw.data));
1203 if (p12FilePw.data != NULL)
1204 PORT_ZFree(p12FilePw.data, PL_strlen(p12FilePw.data));
1205 if (slot)
1206 PK11_FreeSlot(slot);
1207 if (NSS_Shutdown() != SECSuccess) {
1208 pk12uErrno = 1;
1209 }
1210 PL_ArenaFinish();
1211 PR_Cleanup();
1212 return pk12uErrno;
1213 }
1214