1 | /***************************************
2 | $Revision: 1.38 $
3 |
4 | gpg.c - core of the PA module. Contains functions that are used
5 | to check the PGP authentication in a message.
6 |
7 | Status: COMPLETE, REVUED, TESTED
8 |
9 | ******************/ /******************
10 | Filename : gpg.c
11 | Authors : Filippo Portera, Daniele Arena
12 | OSs Tested : Solaris 7
13 | ******************/ /******************
14 | Copyright (c) 2000,2001,2002 RIPE NCC
15 |
16 | All Rights Reserved
17 |
18 | Permission to use, copy, modify, and distribute this software and its
19 | documentation for any purpose and without fee is hereby granted,
20 | provided that the above copyright notice appear in all copies and that
21 | both that copyright notice and this permission notice appear in
22 | supporting documentation, and that the name of the author not be
23 | used in advertising or publicity pertaining to distribution of the
24 | software without specific, written prior permission.
25 |
26 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
28 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
29 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
30 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
31 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 | ***************************************/
33 |
34 | #include "rip.h"
35 |
36 | #include <stdio.h>
37 | #include <stdlib.h>
38 | #include <string.h>
39 | #include <sys/wait.h>
40 | #include <unistd.h>
41 | #include <errno.h>
42 | #include <sys/types.h>
43 | #include <sys/stat.h>
44 | #include <fcntl.h>
45 | #include <time.h>
46 | #include <signal.h>
47 | #include <sys/time.h>
48 | #include <sys/param.h>
49 |
50 | extern char EP_outputPrefix[FILENAME_LENGTH];
51 | extern char EP_keyRing[FILENAME_LENGTH];
52 | extern char EP_gpgcmd[FILENAME_LENGTH];
53 | extern int EP_TreeHeight;
54 | extern int EP_Node_ID;
55 | extern int EP_Debug;
56 | extern char *tmpdir;
57 |
58 | /* static int parseMailBlock_nMsg;
59 | static int parseRecursionLevel; */
60 |
61 | extern int sd1[2];
62 | extern int spawn_job (char *path, char *argv[],
63 | int *in_fd, int *out_fd, int *err_fd);
64 | extern time_t nfslock(char *path, char *namelock, int max_age, int notify);
65 | extern int nfsunlock(char *path, char *namelock, int max_age, time_t birth);
66 |
67 |
68 | static void VerifySignAndExplodeFile(EPNodePtr ptr);
69 | static void GetKeyID(struct ImportKeyObject *iKO);
70 |
71 |
72 | /**************************************
73 | *
74 | * API functions
75 | *
76 | **************************************/
77 |
78 |
79 | /*++++++++++++++++++++++++++++
80 |
81 | Verify a detached PGP signature.
82 |
83 | struct VerifySignObject *vSO The signed object structure to be verified.
84 |
85 | ++++++++++++++++++++++++++++*/
86 |
87 | void PA_VerifySignature(struct VerifySignObject *vSO) {
88 | char *strArgs[10];
89 | char Args0[100];
90 | char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100],
91 | Args6[100], Args7[100];
92 | int gpg_pid;
93 | int gpg_in_fd, out_fd, err_fd;
94 | int status;
95 | /* static int nMsgs = 0; */
96 | char txt[LINE_LENGTH];
97 | char *keyStr;
98 | /* int childRC; */
99 |
100 | int fIn,fOut;
101 | char tmpFileName[100],lfcrStr[10],strIn[10];
102 | char prevChar;
103 |
104 | vSO->type = vSO_Type_Signed;
105 |
106 | strcpy(Args0, "--no-secmem-warning");
107 | strcpy(Args1, "--keyring");
108 | strcpy(Args2, vSO->keyRing);
109 | strcpy(Args3, "-o");
110 | if (!strcmp(vSO->iSigFilename, "")) {
111 | strcpy(Args4, vSO->oStream);
112 | strcpy(Args5, "-d");
113 | strcpy(Args6, vSO->iDocSigFilename);
114 | strArgs[6] = Args6;
115 | strArgs[7] = (char *)0;
116 | } else {
117 | /* change <cr> to <lf>+<cr> to be related-rfc compliant */
118 | fIn=open(vSO->iDocSigFilename,O_RDONLY);
119 | if (fIn==-1) {
120 | ER_perror(FAC_PA, PA_CANTREAD, "can't open %s for reading", vSO->iDocSigFilename);
121 | exit(1);
122 | }
123 | strcpy(tmpFileName,tmpdir);
124 | strcat(tmpFileName,"/patmpXXXXXXX");
125 | fOut=mkstemp(tmpFileName);
126 | if (fOut==-1) {
127 | ER_perror(FAC_PA, PA_NOTEMP, "%s", tmpFileName);
128 | exit(1);
129 | }
130 | prevChar=0;
131 | sprintf(lfcrStr,"%c%c%c",13,10,0);
132 | while(read(fIn,strIn,1)>0)
133 | {
134 | if ((strIn[0]==10)&&(prevChar!=13)) {
135 | write(fOut,lfcrStr,2);
136 | } else {
137 | write(fOut,strIn,1);
138 | }
139 | prevChar=strIn[0];
140 | }
141 | close(fOut);
142 | close(fIn);
143 | /* end change <cr> to <lf>+<cr> to be related-rfc compliant */
144 |
145 | strcpy(Args5, "--verify");
146 | strcpy(Args6, vSO->iSigFilename);
147 | strcpy(Args7, tmpFileName);
148 |
149 | strArgs[6] = Args6;
150 | strArgs[7] = Args7;
151 | strArgs[8] = (char *)0;
152 | strcpy(vSO->oStream, vSO->iDocSigFilename);
153 | }
154 |
155 | strArgs[0] = Args0;
156 | strArgs[1] = Args1;
157 | strArgs[2] = Args2;
158 | strArgs[3] = Args3;
159 | strArgs[4] = Args4;
160 | strArgs[5] = Args5;
161 |
162 | gpg_in_fd = INPUT_FD;
163 | out_fd = OUTPUT_FD;
164 | err_fd = ERROR_FD;
165 | if ( ( gpg_pid = spawn_job (EP_gpgcmd, strArgs,
166 | &gpg_in_fd, &out_fd, &err_fd) ) < 0 )
167 | {
168 | ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
169 | exit(1);
170 | }
171 |
172 | if (waitpid (gpg_pid, &status, 0) < 0)
173 | {
174 | ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
175 | exit(1);
176 | }
177 | unlink(tmpFileName);
178 | if (WIFEXITED(status) == 0)
179 | {
180 | ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
181 | exit(1);
182 | } else {
183 | /* Child exited, checking return code */
184 | /* childRC = (status & 0xF00) >> 8;
185 | if (childRC == 1) {
186 | fprintf (stderr, "Fatal: gpg child return code: %d\n", childRC);
187 | printf ("gpg failure\n");
188 | exit(1);
189 | } */
190 | }
191 |
192 |
193 | /* Parsing gpg output */
194 | vSO->isValid = vSO_KO;
195 | while (fgets (txt, LINE_LENGTH - 1, stdin) != NULL)
196 | {
197 | /* printf ( "GPG output : %s\n", txt ); */
198 | if (strstr(txt, "Good signature") != NULL)
199 | vSO->isValid = vSO_IS_VALID;
200 |
201 | if (strstr(txt, "CRC error") != NULL)
202 | vSO->isValid = vSO_CRC_ERROR;
203 |
204 | if (strstr(txt, "public key not found") != NULL)
205 | vSO->isValid = vSO_NO_PUBLIC_KEY;
206 |
207 | if (strstr(txt, "no valid OpenPGP data found") != NULL)
208 | vSO->isValid = vSO_NO_OPENPGP_DATA;
209 |
210 | if ((keyStr = strstr(txt, "key ID")) != NULL) {
211 | keyStr += 7;
212 | sscanf(keyStr, "%8X\n", &vSO->keyID);
213 | }
214 | }
215 |
216 | if (sd1[0] != 0) close ( sd1[0] );
217 | }
218 |
219 |
220 |
221 | /*++++++++++++++++++++++++++++
222 |
223 | Decrypt a PGP-encrypted file.
224 |
225 | struct ReadCryptedObject *rDO The object to be decrypted
226 |
227 |
228 | Note:
229 | This functions is not used by PA/EP/MM
230 | It can be useful in the future.... (FP)
231 |
232 | ++++++++++++++++++++++++++++*/
233 |
234 | void PA_Decrypt(struct ReadCryptedObject *rDO) {
235 |
236 | char *strArgs[9];
237 | char clearTextExtension[4] = ".gpg";
238 | char Args0[100];
239 | char Args1[100];
240 | char Args2[100];
241 | char Args3[100];
242 | char Args4[100];
243 | char Args5[100];
244 | char Args6[100];
245 | int gpg_pid;
246 | int gpg_in_fd, out_fd, err_fd;
247 | int status;
248 | char txt[LINE_LENGTH];
249 | int childRC;
250 |
251 | strcpy(Args0, "--no-tty");
252 | strcpy(Args1, "--no-secmem-warning");
253 | strcpy(Args2, "--keyring");
254 | strcpy(Args3, rDO->keyRing);
255 | strcpy(Args4, "--output");
256 | strcpy(Args5, strcat(rDO->iFilename, clearTextExtension));
257 | strcpy(Args6, rDO->iFilename);
258 |
259 | strArgs[0] = Args0;
260 | strArgs[1] = Args1;
261 | strArgs[2] = Args2;
262 | strArgs[3] = Args3;
263 | strArgs[4] = Args4;
264 | strArgs[5] = Args5;
265 | strArgs[6] = Args6;
266 | strArgs[7] = (char *) 0;
267 |
268 | gpg_in_fd = INPUT_FD;
269 | out_fd = OUTPUT_FD;
270 | err_fd = ERROR_FD;
271 | if ( ( gpg_pid = spawn_job (EP_gpgcmd, strArgs,
272 | &gpg_in_fd, &out_fd, &err_fd) ) < 0 )
273 | {
274 | ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
275 | exit(1);
276 | }
277 |
278 | if (waitpid (gpg_pid, &status, 0) < 0)
279 | {
280 | ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
281 | exit(1);
282 | }
283 | if (WIFEXITED(status) == 0)
284 | {
285 | ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
286 | exit(1);
287 | } else {
288 | /* Child exited, checking return code */
289 | childRC = (status & 0xF00) >> 8;
290 | if (childRC == 1) {
291 | ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
292 | exit(1);
293 | }
294 | }
295 |
296 |
297 | /* Parsing gpg output */
298 | while (fgets (txt, STRING_LENGTH - 1, stdin) != NULL)
299 | {
300 |
301 | }
302 |
303 | if (sd1[0] != 0) close ( sd1[0] );
304 | }
305 |
306 |
307 |
308 | /*++++++++++++++++++++++++++++
309 |
310 | Import a PGP key.
311 |
312 | struct ImportKeyObject *iKO The structure where the imported key goes
313 |
314 | ++++++++++++++++++++++++++++*/
315 |
316 | void PA_ImportKey(struct ImportKeyObject *iKO) {
317 |
318 | char *strArgs[9];
319 | char Args0[100];
320 | char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100];
321 | int gpg_pid;
322 | int gpg_in_fd, out_fd, err_fd;
323 | int status;
324 | char txt[LINE_LENGTH];
325 | char *keyStr, *pos;
326 | const char lockFilename[] = ".PAlock";
327 | char keyRingLockFile[1000], keyRingPath[1000];
328 | time_t lockBirthDate;
329 | FILE *mystdin;
330 | int childRC;
331 | int key_count;
332 | GList *key_list = NULL;
333 | GList *next = NULL;
334 | struct ImportKeyObject iKO_toBeRemoved;
335 |
336 | iKO->rc = iKO_GENERALFAILURE;
337 |
338 | strcpy(Args0, "--no-tty");
339 | strcpy(Args1, "--no-secmem-warning");
340 | strcpy(Args2, "--keyring");
341 | strcpy(Args3, iKO->keyRing);
342 | strcpy(Args4, "--import");
343 | strcpy(Args5, iKO->iFilename);
344 |
345 | strArgs[0] = Args0;
346 | strArgs[1] = Args1;
347 | strArgs[2] = Args2;
348 | strArgs[3] = Args3;
349 | strArgs[4] = Args4;
350 | strArgs[5] = Args5;
351 | strArgs[6] = (char *)0;
352 |
353 | gpg_in_fd = INPUT_FD;
354 | out_fd = OUTPUT_FD;
355 | err_fd = ERROR_FD;
356 |
357 | /* create lock file filenames for NFS */
358 |
359 | strcpy(keyRingLockFile, iKO->keyRing);
360 | if ((pos = strrchr(keyRingLockFile, '/')) != NULL) {
361 | strcpy(pos + 1, lockFilename);
362 | strcpy(keyRingPath, keyRingLockFile);
363 | keyRingPath[pos - keyRingLockFile] = 0;
364 | } else {
365 | strcpy(keyRingLockFile, lockFilename);
366 | strcpy(keyRingPath, "");
367 | }
368 |
369 | lockBirthDate = nfslock(keyRingPath, (char*)lockFilename, 0, 0);
370 |
371 | if ( ( gpg_pid = spawn_job (EP_gpgcmd, strArgs,
372 | &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
373 | ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
374 | exit(1);
375 | }
376 |
377 | if (waitpid (gpg_pid, &status, 0) < 0)
378 | {
379 | ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
380 | nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);
381 | exit(1);
382 | }
383 |
384 | nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);
385 |
386 | if (WIFEXITED(status) == 0)
387 | {
388 | ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
389 | } else {
390 | /* Child exited, checking return code */
391 | childRC = (status & 0xF00) >> 8;
392 | if (childRC == 1) {
393 | ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
394 | exit(1);
395 | }
396 | }
397 |
398 |
399 | /* Parsing gpg output */
400 | /* while (read(0, txt, 1000) != 0)
401 | fprintf(stderr, "child read %s\n", txt); */
402 |
403 | mystdin = fdopen(0, "r");
404 | iKO->rc = iKO_GENERALFAILURE;
405 | key_count = 0;
406 | while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
407 | {
408 | /* printf ( "GPG output : %s\n", txt ); */
409 |
410 | if ((keyStr = strstr(txt, "imported")) != NULL) {
411 | iKO->rc = iKO_OK;
412 | }
413 |
414 | if ((keyStr = strstr(txt, "CRC error")) != NULL) {
415 | iKO->rc = iKO_CRC_ERROR;
416 | }
417 |
418 | if ((keyStr = strstr(txt, "no valid OpenPGP")) != NULL) {
419 | iKO->rc = iKO_NO_OPENPGP_DATA;
420 | }
421 |
422 | if (((keyStr = strstr(txt, "unchanged")) != NULL) ||
423 | ((keyStr = strstr(txt, "not changed")) != NULL)) {
424 | iKO->rc = iKO_UNCHANGED;
425 | }
426 |
427 | if ((keyStr = strstr(txt, "key")) != NULL) {
428 | keyStr += 4;
429 | sscanf(keyStr, "%8X\n", &iKO->keyID);
430 | key_count++;
431 | /* and put the key ID into the keys list (if it was new to the keyring)*/
432 | if(strstr(txt, "imported") != NULL){
433 | key_list = g_list_append(key_list, GINT_TO_POINTER(iKO->keyID));
434 | }
435 |
436 | }
437 | }
438 |
439 | if (sd1[0] != 0) close ( sd1[0] );
440 |
441 | if(key_count > 1){/* if there were more than one keys imported */
442 | iKO->rc = iKO_MULTIPLE_KEYS; /* this is an error */
443 | /* now, roll-back, remove the added keys from key-ring */
444 | for( next = key_list; next != NULL; next = g_list_next(next) ){
445 | strcpy(iKO_toBeRemoved.keyRing,
446 | iKO->keyRing);
447 | iKO_toBeRemoved.keyID = (u32)(next->data);
448 | PA_RemoveKey_withKeyID(&iKO_toBeRemoved);
449 | }
450 |
451 | }else{
452 | /* Get the finger print */
453 | GetFingerPrint(iKO);
454 | GetKeyOwner(iKO);
455 | }
456 | }
457 |
458 |
459 |
460 | /*++++++++++++++++++++++++++++
461 |
462 | Remove a PGP key.
463 |
464 | struct ImportKeyObject *iKO The structure containing the key to be removed
465 |
466 | ++++++++++++++++++++++++++++*/
467 |
468 | void PA_RemoveKey(struct ImportKeyObject *iKO) {
469 |
470 | char *strArgs[9];
471 | char Args0[100]= "gpg";
472 | char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100], Args6[100], Args7[100];
473 | int gpg_pid;
474 | int gpg_in_fd, out_fd, err_fd;
475 | int status;
476 | char txt[LINE_LENGTH];
477 | char *keyStr, *pos;
478 | const char lockFilename[] = ".PAlock";
479 | char keyRingLockFile[1000], keyRingPath[1000];
480 | time_t lockBirthDate;
481 | FILE *mystdin;
482 | int childRC;
483 |
484 | iKO->rc = iKO_GENERALFAILURE;
485 |
486 | GetKeyID(iKO); /* getting key-id */
487 |
488 | /* printf("Key id = %08lX\n", iKO->keyID); */
489 |
490 | if ((iKO->rc == iKO_OK) || (iKO->rc == iKO_UNCHANGED)) {
491 | strcpy(Args1, "--batch");
492 | strcpy(Args2, "--yes");
493 | strcpy(Args3, "--no-secmem-warning");
494 | strcpy(Args4, "--keyring");
495 | strcpy(Args5, iKO->keyRing);
496 | strcpy(Args6, "--delete-key");
497 | sprintf(Args7, "%08X", iKO->keyID);
498 |
499 | strArgs[0] = Args0;
500 | strArgs[1] = Args1;
501 | strArgs[2] = Args2;
502 | strArgs[3] = Args3;
503 | strArgs[4] = Args4;
504 | strArgs[5] = Args5;
505 | strArgs[6] = Args6;
506 | strArgs[7] = Args7;
507 | strArgs[8] = (char *)0;
508 |
509 |
510 | gpg_in_fd = INPUT_FD;
511 | out_fd = OUTPUT_FD;
512 | err_fd = ERROR_FD;
513 |
514 | /* create lock file filenames for NFS */
515 |
516 | strcpy(keyRingLockFile, iKO->keyRing);
517 | if ((pos = strrchr(keyRingLockFile, '/')) != NULL) {
518 | strcpy(pos + 1, lockFilename);
519 | strcpy(keyRingPath, keyRingLockFile);
520 | keyRingPath[pos - keyRingLockFile] = 0;
521 | } else {
522 | strcpy(keyRingLockFile, lockFilename);
523 | strcpy(keyRingPath, "");
524 | }
525 |
526 | lockBirthDate = nfslock(keyRingPath, (char*)lockFilename, 0, 0);
527 |
528 | if ( ( gpg_pid = spawn_job (EP_gpgcmd, strArgs,
529 | &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
530 | ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
531 | exit(1);
532 | }
533 |
534 | /* printf("Child pid = %d\n", gpg_pid); */
535 |
536 | if (waitpid (gpg_pid, &status, 0) < 0)
537 | {
538 | ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
539 | exit(1);
540 | }
541 |
542 | nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);
543 |
544 | if (WIFEXITED(status) == 0)
545 | {
546 | ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
547 | exit(1);
548 | } else {
549 | /* Child exited, checking return code */
550 | childRC = (status & 0xF00) >> 8;
551 | if (childRC == 1) {
552 | ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
553 | exit(1);
554 | }
555 | }
556 |
557 |
558 | mystdin = fdopen(0, "r");
559 | iKO->rc = iKO_OK;
560 | while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
561 | {
562 | /* printf ( "GPG output : %s\n", txt ); */
563 |
564 | if ((keyStr = strstr(txt, "delete key failed")) != NULL) {
565 | iKO->rc = iKO_GENERALFAILURE;
566 | }
567 | if ((keyStr = strstr(txt, "there is a secret key for this public key")) != NULL) {
568 | iKO->rc = iKO_SECRET_KEY_PRESENT;
569 | }
570 |
571 | }
572 |
573 | if (sd1[0] != 0) close ( sd1[0] );
574 | }
575 | }
576 |
577 | /*++++++++++++++++++++++++++++
578 |
579 | Remove a PGP key, using its KeyID (otherwise it's the same as PA_RemoveKey
580 |
581 | struct ImportKeyObject *iKO The structure containing the key to be removed
582 |
583 | ++++++++++++++++++++++++++++*/
584 |
585 | void PA_RemoveKey_withKeyID(struct ImportKeyObject *iKO) {
586 |
587 | char *strArgs[9];
588 | char Args0[100]= "gpg";
589 | char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100], Args6[100], Args7[100];
590 | int gpg_pid;
591 | int gpg_in_fd, out_fd, err_fd;
592 | int status;
593 | char txt[LINE_LENGTH];
594 | char *keyStr, *pos;
595 | const char lockFilename[] = ".PAlock";
596 | char keyRingLockFile[1000], keyRingPath[1000];
597 | time_t lockBirthDate;
598 | FILE *mystdin;
599 | int childRC;
600 |
601 | iKO->rc = iKO_GENERALFAILURE;
602 |
603 |
604 | strcpy(Args1, "--batch");
605 | strcpy(Args2, "--yes");
606 | strcpy(Args3, "--no-secmem-warning");
607 | strcpy(Args4, "--keyring");
608 | strcpy(Args5, iKO->keyRing);
609 | strcpy(Args6, "--delete-key");
610 | sprintf(Args7, "%08X", iKO->keyID);
611 |
612 | strArgs[0] = Args0;
613 | strArgs[1] = Args1;
614 | strArgs[2] = Args2;
615 | strArgs[3] = Args3;
616 | strArgs[4] = Args4;
617 | strArgs[5] = Args5;
618 | strArgs[6] = Args6;
619 | strArgs[7] = Args7;
620 | strArgs[8] = (char *)0;
621 |
622 |
623 | gpg_in_fd = INPUT_FD;
624 | out_fd = OUTPUT_FD;
625 | err_fd = ERROR_FD;
626 |
627 | /* create lock file filenames for NFS */
628 |
629 | strcpy(keyRingLockFile, iKO->keyRing);
630 | if ((pos = strrchr(keyRingLockFile, '/')) != NULL) {
631 | strcpy(pos + 1, lockFilename);
632 | strcpy(keyRingPath, keyRingLockFile);
633 | keyRingPath[pos - keyRingLockFile] = 0;
634 | } else {
635 | strcpy(keyRingLockFile, lockFilename);
636 | strcpy(keyRingPath, "");
637 | }
638 |
639 | lockBirthDate = nfslock(keyRingPath, (char*)lockFilename, 0, 0);
640 |
641 |
642 | if ( ( gpg_pid = spawn_job (EP_gpgcmd, strArgs,
643 | &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
644 | ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
645 | exit(1);
646 | }
647 |
648 | /* printf("Child pid = %d\n", gpg_pid); */
649 |
650 | if (waitpid (gpg_pid, &status, 0) < 0)
651 | {
652 | ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
653 | exit(1);
654 | }
655 |
656 | nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);
657 |
658 | if (WIFEXITED(status) == 0)
659 | {
660 | ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
661 | exit(1);
662 | } else {
663 | /* Child exited, checking return code */
664 | childRC = (status & 0xF00) >> 8;
665 | if (childRC == 1) {
666 | ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
667 | exit(1);
668 | }
669 | }
670 |
671 |
672 | mystdin = fdopen(0, "r");
673 | iKO->rc = iKO_OK;
674 | while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
675 | {
676 | /* printf ( "GPG output : %s\n", txt ); */
677 |
678 | if ((keyStr = strstr(txt, "delete key failed")) != NULL) {
679 | iKO->rc = iKO_GENERALFAILURE;
680 | }
681 | if ((keyStr = strstr(txt, "there is a secret key for this public key")) != NULL) {
682 | iKO->rc = iKO_SECRET_KEY_PRESENT;
683 | }
684 |
685 | }
686 |
687 | if (sd1[0] != 0) close ( sd1[0] );
688 |
689 | }
690 |
691 |
692 |
693 |
694 | /*++++++++++++++++++++++++++++
695 |
696 | Parse a file and look for PGP-signed elements inside.
697 |
698 | EPNodePtr ptr The pointer to the EP treenode containing the file to be parsed.
699 |
700 | ++++++++++++++++++++++++++++*/
701 |
702 | EPNodePtr PA_ParseMessage(EPNodePtr ptr) {
703 |
704 | /* Assumptions:
705 | - ptr is describing a text file, not MIME
706 | - input file is broken down to pieces, plain text or PGP blocks
707 | - if input file is doesn't have any PGP block, this is a leaf
708 | - otherwise send each block to the proper handler.
709 | */
710 |
711 | FILE *fin, *fout;
712 | char txt[MAX_LINE_BUF], *strptr;
713 | /* char blockFilename[LINE_LENGTH]; */
714 | const char PGP_prefix_msg[] = "-----BEGIN PGP MESSAGE";
715 | const char PGP_suffix_msg[] = "-----END PGP MESSAGE";
716 | const char PGP_prefix_signed[] = "-----BEGIN PGP SIGNED MESSAGE";
717 | const char PGP_suffix_signature[] = "-----END PGP SIGNATURE";
718 | int found_prefix = 0, found_suffix = 0;
719 | EPNodePtr p = ptr, prev = ptr;
720 | int end_of_fin = 0, text_block = 1;
721 |
722 |
723 | ER_dbg_va (FAC_PA, ASP_PA_GEN, "Entering PA_ParseMessage...");
724 |
725 |
726 | if ((fin = fopen(ptr->file, "r")) != NULL) {
727 |
728 | do {
729 | /* this is needed because a text block parser ends when it finds
730 | a PGP prefix, so we already have a txt buffer. */
731 |
732 | if (!text_block || (prev == ptr)) {
733 | strptr = fgets(txt, MAX_LINE_BUF, fin);
734 | if (strptr == NULL ) end_of_fin = 1;
735 | }
736 |
737 | if (!end_of_fin && (found_prefix || (strstr(txt, PGP_prefix_msg) != NULL) ||
738 | (strstr(txt, PGP_prefix_signed) != NULL))) {
739 | /* PGP block */
740 | found_prefix = 1;
741 | text_block = 0;
742 |
743 | p = EP_DefineNewNode(++EP_Node_ID, vS_TO_BE_PGPVERIFIED,
744 | ptr->MIMEContentType, ptr->strMIMEContentType, 0);
745 |
746 | if (prev != ptr)
747 | prev->next = p;
748 | else
749 | ptr->inner = p;
750 |
751 | if ((fout = fopen(p->file, "w")) != NULL ) {
752 | fputs(txt, fout);
753 | /* To be replaced by fwrite(), more efficient */
754 | while ((found_prefix != found_suffix) &&
755 | ((strptr = fgets(txt, MAX_LINE_BUF, fin)) != NULL)) {
756 | if (strstr(txt, PGP_prefix_msg) != NULL) found_prefix++;
757 | if (strstr(txt, PGP_prefix_signed) != NULL) found_prefix++;
758 | if (strstr(txt, PGP_suffix_msg) != NULL) found_suffix++;
759 | if (strstr(txt, PGP_suffix_signature) != NULL) found_suffix++;
760 | fputs(txt, fout);
761 | }
762 | if (strptr == NULL ) end_of_fin = 1;
763 |
764 | fclose(fout);
765 |
766 | if (found_prefix == found_suffix) {
767 | found_prefix = found_suffix = 0;
768 |
769 | VerifySignAndExplodeFile(p);
770 |
771 | /* Called form EP_ParseMail or EP_PArseText ? */
772 |
773 | if (strstr(EP_outputPrefix, "EPMtmp") != NULL)
774 | {
775 | ER_dbg_va (FAC_PA, ASP_PA_GEN, "Found prefix and suffix; calling EP_MIMEParse...");
776 | EP_MIMEParse(p);
777 | }
778 | else
779 | PA_ParseMessage(p);
780 |
781 | prev = p;
782 | } else {
783 | /* Wrong PGP delimiters order. */
784 | p->isValidPGPSignature = vS_UNMATCHED_PGP_DELIMITERS;
785 | }
786 | } else {
787 | p->isValidPGPSignature = vS_UNABLE_TO_WRITE_FILE;
788 | return p;
789 | }
790 |
791 | } else {
792 | /* Clear text block */
793 |
794 | text_block = 1;
795 |
796 | if (strptr == NULL) end_of_fin = 1;
797 | else {
798 | p = EP_DefineNewNode(++EP_Node_ID, vS_IS_NOT_PGP,
799 | ptr->MIMEContentType,
800 | ptr->strMIMEContentType, 0);
801 |
802 | if (prev != ptr)
803 | prev->next = p;
804 | else
805 | ptr->inner = p;
806 |
807 | if ((fout = fopen(p->file, "w")) != NULL ) {
808 | fputs(txt, fout);
809 | /* To be replaced by fwrite(), more efficient */
810 | while ((!found_prefix &&
811 | (strptr = fgets(txt, MAX_LINE_BUF, fin)) != NULL)) {
812 | if ((strstr(txt, PGP_prefix_msg) != NULL) ||
813 | (strstr(txt, PGP_prefix_signed) != NULL)) found_prefix++;
814 | else
815 | fputs(txt, fout);
816 | }
817 | if (strptr == NULL ) end_of_fin = 1;
818 |
819 | fclose(fout);
820 |
821 | /* Check if the blockfile is finished and this is the first
822 | segment. If so this is a text leaf */
823 | if (found_prefix || (prev != p)) {
824 | if (prev->MIMEContentType == -1)
825 | {
826 | if (strstr(EP_outputPrefix, "EPMtmp") != NULL)
827 | {
828 | ER_dbg_va (FAC_PA, ASP_PA_GEN, "PA_ParseMessage: sending to EP_MIMEParse");
829 | EP_MIMEParse(p);
830 | }
831 | else
832 | PA_ParseMessage(p);
833 | }
834 |
835 | prev = p;
836 | }
837 |
838 | } else {
839 | p->isValidPGPSignature = vS_UNABLE_TO_WRITE_FILE;
840 | return p;
841 | }
842 | }
843 | }
844 | } while (!end_of_fin);
845 | } else {
846 | p->isValidPGPSignature = vS_NO_IN_FILES;
847 | }
848 |
849 | return ptr;
850 | }
851 |
852 |
853 | /**************************************
854 | *
855 | * Internal functions
856 | *
857 | **************************************/
858 |
859 |
860 |
861 | /*++++++++++++++++++++++++++++
862 |
863 | Get the fingerprint of a PGP key.
864 |
865 | ImportKeyObject *iKO The imported key object
866 |
867 | ++++++++++++++++++++++++++++*/
868 |
869 | void GetFingerPrint(struct ImportKeyObject *iKO) {
870 |
871 | char *strArgs[9];
872 | char Args0[100] ;
873 | char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100];
874 | int gpg_pid;
875 | int gpg_in_fd, out_fd, err_fd;
876 | int status;
877 | char txt[LINE_LENGTH];
878 | char *keyStr;
879 | FILE *mystdin;
880 | int childRC;
881 |
882 | strcpy(Args0, "--no-tty");
883 | strcpy(Args1, "--no-secmem-warning");
884 | strcpy(Args2, "--keyring");
885 | strcpy(Args3, iKO->keyRing);
886 | strcpy(Args4, "--fingerprint");
887 | sprintf(Args5, "%08X", iKO->keyID);
888 |
889 | strArgs[0] = Args0;
890 | strArgs[1] = Args1;
891 | strArgs[2] = Args2;
892 | strArgs[3] = Args3;
893 | strArgs[4] = Args4;
894 | strArgs[5] = Args5;
895 | strArgs[6] = (char *)0;
896 |
897 | gpg_in_fd = INPUT_FD;
898 | out_fd = OUTPUT_FD;
899 | err_fd = ERROR_FD;
900 |
901 | /* create lock file filenames for NFS */
902 |
903 | if ( ( gpg_pid = spawn_job (EP_gpgcmd, strArgs,
904 | &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
905 | ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
906 | exit(1);
907 | }
908 |
909 | if (waitpid (gpg_pid, &status, 0) < 0)
910 | {
911 | ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
912 | exit(1);
913 | }
914 |
915 | if (WIFEXITED(status) == 0)
916 | {
917 | ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
918 | exit(1);
919 | } else {
920 | /* Child exited, checking return code */
921 | childRC = (status & 0xF00) >> 8;
922 | if (childRC == 1) {
923 | ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
924 | exit(1);
925 | }
926 | }
927 |
928 |
929 | mystdin = fdopen(0, "r");
930 | while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
931 | {
932 | /* printf ( "GPG output : %s\n", txt ); */
933 |
934 | if ((keyStr = strstr(txt, "Key fingerprint =")) != NULL) {
935 | strcpy(iKO->fingerPrint, keyStr + 18);
936 | iKO->fingerPrint[strlen(iKO->fingerPrint)-1] = 0;
937 | }
938 |
939 | if ((keyStr = strstr(txt, "key")) != NULL) {
940 | keyStr += 4;
941 | sscanf(keyStr, "%8X\n", &iKO->keyID);
942 | }
943 | }
944 |
945 | if (sd1[0] != 0) close ( sd1[0] );
946 | }
947 |
948 |
949 |
950 | /*++++++++++++++++++++++++++++
951 |
952 | Get the owner of a PGP key.
953 |
954 | ImportKeyObject *iKO The imported key object
955 |
956 | ++++++++++++++++++++++++++++*/
957 |
958 | void GetKeyOwner(struct ImportKeyObject *iKO) {
959 |
960 | char *strArgs[9];
961 | char Args0[100] ;
962 | char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100];
963 | int gpg_pid;
964 | int gpg_in_fd, out_fd, err_fd;
965 | int status;
966 | char txt[LINE_LENGTH];
967 | char *keyStr;
968 | FILE *mystdin;
969 | int childRC;
970 |
971 | strcpy(Args0, "--no-tty");
972 | strcpy(Args1, "--no-secmem-warning");
973 | strcpy(Args2, "--keyring");
974 | strcpy(Args3, iKO->keyRing);
975 | strcpy(Args4, "--fingerprint");
976 | sprintf(Args5, "%08X", iKO->keyID);
977 |
978 | strArgs[0] = Args0;
979 | strArgs[1] = Args1;
980 | strArgs[2] = Args2;
981 | strArgs[3] = Args3;
982 | strArgs[4] = Args4;
983 | strArgs[5] = Args5;
984 | strArgs[6] = (char *)0;
985 |
986 | gpg_in_fd = INPUT_FD;
987 | out_fd = OUTPUT_FD;
988 | err_fd = ERROR_FD;
989 |
990 | /* create lock file filenames for NFS */
991 |
992 | if ( ( gpg_pid = spawn_job (EP_gpgcmd, strArgs,
993 | &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
994 | ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
995 | exit(1);
996 | }
997 |
998 | if (waitpid (gpg_pid, &status, 0) < 0)
999 | {
1000 | ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
1001 | exit(1);
1002 | }
1003 |
1004 | if (WIFEXITED(status) == 0)
1005 | {
1006 | ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
1007 | exit(1);
1008 | } else {
1009 | /* Child exited, checking return code */
1010 | childRC = (status & 0xF00) >> 8;
1011 | if (childRC == 1) {
1012 | ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
1013 | exit(1);
1014 | }
1015 | }
1016 |
1017 |
1018 | mystdin = fdopen(0, "r");
1019 | while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
1020 | {
1021 | /* printf ( "GPG output : %s\n", txt ); */
1022 |
1023 | if ((keyStr = strstr(txt, "pub "/*"Key fingerprint ="*/)) == txt /*!= NULL*/) {
1024 | strcpy(iKO->keyOwner, keyStr + 30);
1025 | iKO->keyOwner[strlen(iKO->keyOwner)-1] = 0;
1026 | }
1027 |
1028 | if ((keyStr = strstr(txt, "key")) != NULL) {
1029 | keyStr += 4;
1030 | sscanf(keyStr, "%8X\n", &iKO->keyID);
1031 | }
1032 | }
1033 |
1034 | if (sd1[0] != 0) close ( sd1[0] );
1035 | }
1036 |
1037 |
1038 |
1039 |
1040 | /*++++++++++++++++++++++++++++
1041 |
1042 | Verify the PGP signature and extract the signed part in a file.
1043 |
1044 | EPNodePtr ptr The pointer to the EP treenode containing the originating file
1045 |
1046 | ++++++++++++++++++++++++++++*/
1047 |
1048 | void VerifySignAndExplodeFile(EPNodePtr ptr) {
1049 | char *strArgs[10];
1050 | char Args0[100];
1051 | char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100],
1052 | Args6[100];
1053 | int gpg_pid;
1054 | int gpg_in_fd, out_fd, err_fd;
1055 | int status;
1056 | char txt[LINE_LENGTH];
1057 | /* char hostname[MAXHOSTNAMELEN]; */
1058 | char oFile[FILENAME_LENGTH];
1059 | char *keyStr;
1060 | /* int childRC; */
1061 |
1062 | sprintf(oFile, "%s.%d.exp", ptr->file, ptr->nodeID);
1063 |
1064 | strcpy(Args0, "--no-secmem-warning");
1065 | strcpy(Args1, "--keyring");
1066 | strcpy(Args2, EP_keyRing);
1067 | strcpy(Args3, "-o");
1068 | strcpy(Args4, oFile);
1069 | strcpy(Args5, "-d");
1070 | strcpy(Args6, ptr->file);
1071 | strArgs[6] = Args6;
1072 | strArgs[7] = (char *)0;
1073 |
1074 | strArgs[0] = Args0;
1075 | strArgs[1] = Args1;
1076 | strArgs[2] = Args2;
1077 | strArgs[3] = Args3;
1078 | strArgs[4] = Args4;
1079 | strArgs[5] = Args5;
1080 |
1081 | gpg_in_fd = INPUT_FD;
1082 | out_fd = OUTPUT_FD;
1083 | err_fd = ERROR_FD;
1084 | if ( ( gpg_pid = spawn_job (EP_gpgcmd, strArgs,
1085 | &gpg_in_fd, &out_fd, &err_fd) ) < 0 )
1086 | {
1087 | ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
1088 | exit(1);
1089 | }
1090 |
1091 | if (waitpid (gpg_pid, &status, 0) < 0)
1092 | {
1093 | ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
1094 | exit(1);
1095 | }
1096 | if (WIFEXITED(status) == 0)
1097 | {
1098 | ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
1099 | exit(1);
1100 | } else {
1101 | /* Child exited, checking return code */
1102 | /* childRC = (status & 0xF00) >> 8;
1103 | if (childRC == 1) {
1104 | fprintf (stderr, "Fatal: gpg child return code: %d\n", childRC);
1105 | printf ("gpg failure\n");
1106 | exit(1);
1107 | } */
1108 | }
1109 |
1110 |
1111 | /* Parsing gpg output */
1112 | ptr->isValidPGPSignature = vS_KO;
1113 | while (fgets (txt, LINE_LENGTH - 1, stdin) != NULL)
1114 | {
1115 | /* printf ( "GPG output : %s\n", txt ); */
1116 | if (strstr(txt, "Good signature") != NULL)
1117 | ptr->isValidPGPSignature = vS_IS_VALID;
1118 |
1119 | if (strstr(txt, "CRC error") != NULL)
1120 | ptr->isValidPGPSignature = vS_CRC_ERROR;
1121 |
1122 | if (strstr(txt, "public key not found") != NULL)
1123 | ptr->isValidPGPSignature = vS_NO_PUBLIC_KEY;
1124 |
1125 | if (strstr(txt, "no valid OpenPGP data found") != NULL)
1126 | ptr->isValidPGPSignature = vS_NO_OPENPGP_DATA;
1127 |
1128 | if ((keyStr = strstr(txt, "key ID")) != NULL) {
1129 | keyStr += 7;
1130 | sscanf(keyStr, "%8X\n", &ptr->keyID);
1131 | }
1132 | }
1133 |
1134 | unlink(ptr->file);
1135 | UT_free(ptr->file);
1136 | ptr->file = UT_strdup(oFile);
1137 | if (sd1[0] != 0) close ( sd1[0] );
1138 | }
1139 |
1140 |
1141 | /*++++++++++++++++++++++++++++
1142 |
1143 | Get the KeyID of a PGP key.
1144 |
1145 | struct ImportKeyObject *iKO The structure containing the key of which we want the KeyID
1146 |
1147 | ++++++++++++++++++++++++++++*/
1148 |
1149 | void GetKeyID(struct ImportKeyObject *iKO) {
1150 |
1151 | char *strArgs[9];
1152 | char Args0[100];
1153 | char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100];
1154 | int gpg_pid;
1155 | int gpg_in_fd, out_fd, err_fd;
1156 | int status;
1157 | char txt[LINE_LENGTH];
1158 | char *keyStr, *pos;
1159 | const char lockFilename[] = ".PAlock";
1160 | char keyRingLockFile[1000], keyRingPath[1000];
1161 | time_t lockBirthDate;
1162 | FILE *mystdin;
1163 | int childRC;
1164 |
1165 | iKO->rc = iKO_GENERALFAILURE;
1166 |
1167 | strcpy(Args0, "--no-tty");
1168 | strcpy(Args1, "--no-secmem-warning");
1169 | strcpy(Args2, "--keyring");
1170 | strcpy(Args3, iKO->keyRing);
1171 | strcpy(Args4, "--import");
1172 | strcpy(Args5, iKO->iFilename);
1173 |
1174 | strArgs[0] = Args0;
1175 | strArgs[1] = Args1;
1176 | strArgs[2] = Args2;
1177 | strArgs[3] = Args3;
1178 | strArgs[4] = Args4;
1179 | strArgs[5] = Args5;
1180 | strArgs[6] = (char *)0;
1181 |
1182 | gpg_in_fd = INPUT_FD;
1183 | out_fd = OUTPUT_FD;
1184 | err_fd = ERROR_FD;
1185 |
1186 | /* create lock file filenames for NFS */
1187 |
1188 | strcpy(keyRingLockFile, iKO->keyRing);
1189 | if ((pos = strrchr(keyRingLockFile, '/')) != NULL) {
1190 | strcpy(pos + 1, lockFilename);
1191 | strcpy(keyRingPath, keyRingLockFile);
1192 | keyRingPath[pos - keyRingLockFile] = 0;
1193 | } else {
1194 | strcpy(keyRingLockFile, lockFilename);
1195 | strcpy(keyRingPath, "");
1196 | }
1197 |
1198 | lockBirthDate = nfslock(keyRingPath, (char*)lockFilename, 0, 0);
1199 |
1200 | if ( ( gpg_pid = spawn_job (EP_gpgcmd, strArgs,
1201 | &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
1202 | ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
1203 | exit(1);
1204 | }
1205 |
1206 | if (waitpid (gpg_pid, &status, 0) < 0)
1207 | {
1208 | ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
1209 | nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);
1210 | exit(1);
1211 | }
1212 |
1213 | nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);
1214 |
1215 | if (WIFEXITED(status) == 0)
1216 | {
1217 | ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
1218 | exit(1);
1219 | } else {
1220 | /* Child exited, checking return code */
1221 | childRC = (status & 0xF00) >> 8;
1222 | if (childRC == 1) {
1223 | ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
1224 | exit(1);
1225 | }
1226 | }
1227 |
1228 |
1229 | /* Parsing gpg output */
1230 | /* while (read(0, txt, 1000) != 0)
1231 | fprintf(stderr, "child read %s\n", txt); */
1232 |
1233 | mystdin = fdopen(0, "r");
1234 | iKO->rc = iKO_GENERALFAILURE;
1235 | while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
1236 | {
1237 | /* printf ( "GPG output : %s\n", txt ); */
1238 |
1239 | if ((keyStr = strstr(txt, "imported")) != NULL) {
1240 | iKO->rc = iKO_OK;
1241 | }
1242 |
1243 | if ((keyStr = strstr(txt, "CRC error")) != NULL) {
1244 | iKO->rc = iKO_CRC_ERROR;
1245 | }
1246 |
1247 | if ((keyStr = strstr(txt, "no valid OpenPGP")) != NULL) {
1248 | iKO->rc = iKO_NO_OPENPGP_DATA;
1249 | }
1250 |
1251 | if (((keyStr = strstr(txt, "unchanged")) != NULL) ||
1252 | ((keyStr = strstr(txt, "not changed")) != NULL)) {
1253 | iKO->rc = iKO_UNCHANGED;
1254 | }
1255 |
1256 | if ((keyStr = strstr(txt, "gpg: key ")) != NULL) {
1257 | keyStr += 9;
1258 | sscanf(keyStr, "%8X\n", &iKO->keyID);
1259 | }
1260 | }
1261 |
1262 | if (sd1[0] != 0) close ( sd1[0] );
1263 |
1264 | }
1265 |