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 |