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