home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1998 February / PCOnline_02_1998.iso / filesbbs / os2 / pgp263.arj / PGP263I.SRC / PGP263II.ZIP / contrib / pgpsort / pgpsort.c
C/C++ Source or Header  |  1995-10-01  |  14KB  |  486 lines

  1.  /******************************************************
  2.  *                                                     *
  3.  *     PGPSort: a utility to sort PGP 2.x keyrings     *
  4.  *               .                                     *
  5.  *  (c)1995 by Stale Schumacher <stale@hypnotech.com>  *
  6.  *              Version 1.01 - 1995/09/04              *
  7.  *                                                     *
  8.  *  This program is placed in the public domain and    *
  9.  *  may be freely distributed and modified, as long    *
  10.  *  as this copyright notice remains intact. The code  *
  11.  *  was written from scratch based on the PGP 2.x      *
  12.  *  format specifications, and contains no source      *
  13.  *  from the PGP distribution. Based on a TurboPascal  *
  14.  *  implementation by the same author.                 *
  15.  *                                                     *
  16.  *  DISCLAIMER: PGPSort will always make a backup of   *
  17.  *  the keyring before sorting it, but the author      *
  18.  *  cannot take any responsibility for damage that     *
  19.  *  the program may cause. Use PGPSort at your own     *
  20.  *  risk!                                              *
  21.  *                                                     *
  22.  *  NOTE FOR PROGRAMMERS: This program was written     *
  23.  *  for portability, _not_ for speed! :-)              *
  24.  *                                                     *
  25.  ******************************************************/
  26.  
  27. #include <stdlib.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <ctype.h>
  31.  
  32. #define MAXKEYS 20000  /* Increase this if necessary */
  33.  
  34. #ifndef TRUE
  35. #define FALSE 0
  36. #define TRUE  !FALSE
  37. #endif
  38.  
  39. #ifndef SEEK_SET
  40. #define SEEK_SET 0
  41. #endif
  42.  
  43. #ifndef MIN
  44. #define MIN(a,b) ((a)<(b)?(a):(b))
  45. #endif
  46.  
  47. #ifndef FILENAME_MAX
  48. #include <sys/param.h>
  49. #ifdef MAXPATHLEN
  50. #define FILENAME_MAX MAXPATHLEN
  51. #endif
  52. #endif
  53.  
  54. typedef unsigned char  byte;
  55. typedef unsigned char  boolean;
  56. typedef unsigned short word16;
  57. typedef unsigned long  word32;
  58.  
  59. #define CTB_PUBKEY 24
  60. #define CTB_SECKEY 20
  61. #define CTB_USERID 52
  62. #define CTB_TRUST  48
  63. #define CTB_SIG     8
  64.  
  65. #define ERR_OK                0
  66. #define ERR_ILLEGAL_PARAMS    1
  67. #define ERR_FILE_NOT_OPEN     2
  68. #define ERR_NOT_A_KEYRING     3
  69. #define ERR_COULD_NOT_BACKUP  4
  70. #define ERR_KEYRING_CORRUPT   5
  71. #define ERR_OUT_OF_MEMORY     6
  72.  
  73. #define SORT_NOSORT     0
  74. #define SORT_ON_USERID  1
  75. #define SORT_ON_KEYID   2
  76. #define SORT_ON_DATE    3
  77. #define SORT_ON_SIZE    4
  78.  
  79. #define ASCENDING  TRUE
  80. #define DESCENDING FALSE
  81.  
  82. #define BADKEY_NO_USERID     0
  83. #define BADKEY_BOGUS_USERID  1
  84. #define BADKEY_SECRET_KEY    2
  85.  
  86. struct keydata {
  87.    long    fpos;
  88.    word32  length,
  89.        keyid,
  90.        date;
  91.    word16  size;
  92.    char    userid[26];
  93.    boolean secret,
  94.            removed;
  95. };
  96.  
  97. struct  keydata *key[MAXKEYS];
  98. int     keys = 0;
  99. int     sort_criterion;
  100. boolean sort_order;
  101. boolean remove_bad_keys;
  102.  
  103. word32 get_packet_length(byte ctb, FILE *fp)
  104. {
  105.    byte   l[4] = {1,2,4,0},
  106.       llength = l[ctb & 0x03],
  107.       i;
  108.    word32 length = 0;
  109.  
  110.    for (i=0; i<llength; i++)
  111.       length = (length << 8) + (byte) fgetc(fp);
  112.    return length;
  113. }
  114.  
  115. word32 read_word32(FILE *fp)
  116. {
  117.    int    i;
  118.    word32 word;
  119.  
  120.    for (i=0; i<32/8; i++)
  121.       word = (word << 8) + (byte) fgetc(fp);
  122.    return word;
  123. }
  124.  
  125. word16 read_word16(FILE *fp)
  126. {
  127.    int    i;
  128.    word16 word;
  129.  
  130.    for (i=0; i<16/8; i++)
  131.       word = (word << 8) + (byte) fgetc(fp);
  132.    return word;
  133. }
  134.  
  135. char *strupper(char *s)
  136. {
  137.    char *p;
  138.    for (p=s; *p; p++)
  139.       *p=toupper(*p);
  140.    return s;
  141. }
  142.  
  143. char *strend(char *s)
  144. {
  145.    char *p;
  146.    for (p=s; *p; p++);
  147.    return p;
  148. }
  149.  
  150. char *strstrip(char *s)
  151. {
  152.    char *p;
  153. #  define stripchar(c) (c==' ' || c=='>' || c=='-')
  154.  
  155.    p=strend(s);
  156.    for (p--; p >= s && stripchar(*p); p--)
  157.       *p='\0';
  158.    return s;
  159. }
  160.  
  161. char *force_extension(char *filename, char *extension)
  162. {
  163.    char *p;
  164.    p=strrchr(filename, '.');
  165.    if (!p) p=strend(filename);
  166.    strcpy(p, extension);
  167.    return filename;
  168. }
  169.  
  170. char *add_slash(char *directory)
  171. {
  172.    char *p;
  173.    p=strend(directory); p--;
  174.    if (*p != '/' && strchr(directory, '/'))
  175.       strcat(directory, "/");   /* Unix */
  176.    if (*p != '\\' && strchr(directory, '\\'))
  177.       strcat(directory, "\\");  /* MS-DOS */
  178.    return directory;
  179. }
  180.  
  181. void prompt_for_removal(int keynr, char *userid, int cause)
  182. {
  183.    char date[11];
  184.    int  c;
  185.  
  186.    switch (cause) {
  187.       case BADKEY_NO_USERID:
  188.          printf("The following key has no attached userID:\n"); break;
  189.       case BADKEY_BOGUS_USERID:
  190.          printf("The following key seems to have a bogus user ID:\n"); break;
  191.       case BADKEY_SECRET_KEY:
  192.          printf("The keyring contains a secret key:\n"); break;
  193.    }
  194.    if (key[keynr]->secret)
  195.      printf("sec  ");
  196.    else
  197.      printf("pub  ");
  198.    strftime(date,sizeof(date),"%Y/%m/%d",gmtime(&key[keynr]->date));
  199.    printf("%4d/%8lX %s %s\n",key[keynr]->size,key[keynr]->keyid,date,userid);
  200.    printf("Remove it <y/N>? ");
  201.    fflush(stdin); c = getchar();
  202.    key[keynr]->removed = (c=='y' || c=='Y');
  203.    printf("\n");
  204. }
  205.  
  206. char *get_username(char *userid)
  207. {
  208.    char s[256];
  209.    char *p = userid;
  210.  
  211.    /* Isolate name from email address, telephone number etc. */
  212.    while (isalpha(*p) || isspace(*p) || *p=='.' || *p=='-') p++;
  213.    if (p > userid) *p='\0';
  214.    strupper(strstrip(userid));
  215.  
  216.    /* Derive name from email address? */
  217.    if (*userid == '<') {
  218.       strcpy(userid, userid+1);
  219.    }
  220.  
  221.    /* Remove titles */
  222.    p=strend(userid);
  223.    if (!strcmp(p-2, " I")) *(p-2)='\0';
  224.    if (!strcmp(p-3, " II")) *(p-3)='\0';
  225.    if (!strcmp(p-4, " III")) *(p-4)='\0';
  226.    if (!strcmp(p-4, " JR.")) *(p-4)='\0';
  227.    if (!strcmp(p-5, " M.D.")) *(p-5)='\0';
  228.  
  229.    /* Swap first and last names */
  230.    if ((p = strrchr(userid, ' ')) != NULL) {
  231.       strcpy(s,p+1);
  232.       *p = '\0';
  233.       strcpy(userid, strcat(s, userid));
  234.    }
  235.  
  236.    /* Return the user name on a form that may easily be sorted */
  237.    return userid;
  238. }
  239.  
  240. int keycmp(struct keydata *key1, struct keydata *key2)
  241. {
  242.    int c;
  243.    switch (sort_criterion) {
  244.       case SORT_ON_USERID: c = strcmp(key1->userid, key2->userid) ; break;
  245.       case SORT_ON_KEYID : c = key1->keyid < key2->keyid ? -1 : +1; break;
  246.       case SORT_ON_SIZE  : c = key1->size  < key2->size  ? -1 : +1; break;
  247.       case SORT_ON_DATE  : c = key1->date  < key2->date  ? -1 : +1; break;
  248.    }
  249.    if (sort_order==DESCENDING) c = -c;
  250.    return c;
  251. }
  252.  
  253. void swap(struct keydata *v[], int i, int j)
  254. {
  255.    struct keydata *temp;
  256.    temp = v[i]; v[i] = v[j]; v[j] = temp;
  257. }
  258.  
  259. void sort_keydata(struct keydata *v[], int left, int right)
  260. {
  261.    int i, last;
  262.    if (left >= right) return;
  263.    swap(v, left, (left+right)/2);
  264.    last = left;
  265.    for (i = left+1; i <= right; i++)
  266.       if (keycmp(v[i], v[left]) < 0)
  267.      swap(v, ++last, i);
  268.    swap(v, left, last);
  269.    sort_keydata(v, left, last-1);
  270.    sort_keydata(v, last+1, right);
  271. }
  272.  
  273. int sort_keyring(char *keyring)
  274. {
  275.    FILE    *ifp,
  276.        *ofp;
  277.    long    count,
  278.        fpos = 0L,
  279.        ctbpos = 0L;
  280.    byte    ctb;
  281.    word32  packet_length;
  282.    char    userid[256],
  283.        bakring[FILENAME_MAX];
  284.    boolean is_pubring,
  285.            first_userid = FALSE;
  286.    byte    buf[256];
  287.    int     i,
  288.        status = ERR_OK;
  289.  
  290. #  define error(s) {status = s; goto end;}
  291.  
  292.    /* Read through the whole keyring and gather keydata */
  293.    if (!(ifp = fopen(keyring,"rb")))
  294.       return ERR_FILE_NOT_OPEN;
  295.    count = fread(&ctb, 1, 1, ifp);
  296.    if ((ctb & 60) == CTB_PUBKEY)
  297.       is_pubring = TRUE;
  298.    else if ((ctb & 60) == CTB_SECKEY)
  299.       is_pubring = FALSE;
  300.    else
  301.       error(ERR_NOT_A_KEYRING);
  302.    while (count > 0) {
  303.       if (!(ctb & 0x80))
  304.      error(ERR_KEYRING_CORRUPT);
  305.       packet_length = get_packet_length(ctb, ifp);
  306.       fpos = ftell(ifp);
  307.       ctb = (ctb & 60);
  308.       switch (ctb) {
  309.      case CTB_PUBKEY:
  310.      case CTB_SECKEY:
  311.             if (first_userid && remove_bad_keys && (!key[keys-1]->removed))
  312.                prompt_for_removal(keys-1,"",BADKEY_NO_USERID);
  313.         if (keys == MAXKEYS)
  314.            error(ERR_OUT_OF_MEMORY);
  315.         if (!(key[keys] = malloc(sizeof(struct keydata))))
  316.            error(ERR_OUT_OF_MEMORY);
  317.         if (keys>0) key[keys-1]->length = ctbpos - key[keys-1]->fpos;
  318.         key[keys]->fpos = ctbpos;
  319.         key[keys]->length = packet_length;
  320.         fseek(ifp, fpos+1, SEEK_SET); key[keys]->date = read_word32(ifp);
  321.         fseek(ifp, fpos+8, SEEK_SET); key[keys]->size = read_word16(ifp);
  322.         fseek(ifp, fpos+6+(key[keys]->size+7)/8, SEEK_SET);
  323.         key[keys]->keyid = read_word32(ifp);
  324.             key[keys]->removed = FALSE;
  325.             key[keys]->secret = (ctb==CTB_SECKEY);
  326.         first_userid = TRUE;
  327.         keys++;
  328.         break;
  329.      case CTB_USERID:
  330.         fread(&userid, packet_length, 1, ifp);
  331.         userid[packet_length]='\0';
  332.             if (remove_bad_keys && (!key[keys-1]->removed)) {
  333.                if (strstr(userid,"FUCK") || strstr(userid,"DICK") ||
  334.                   strstr(userid,"SHIT") || strstr(userid,"BEGIN") ||
  335.                   strstr(userid,"***") || strstr(userid,"@whitehouse.gov") ||
  336.                   strlen(userid) > 150)
  337.                   prompt_for_removal(keys-1,userid,BADKEY_BOGUS_USERID);
  338.         }
  339.         if (first_userid) {
  340.                if (key[keys-1]->secret && remove_bad_keys && (!key[keys-1]->removed)
  341.                   && is_pubring)
  342.                   prompt_for_removal(keys-1,userid,BADKEY_SECRET_KEY);
  343.            strncpy(key[keys-1]->userid, get_username(userid),
  344.                sizeof(key[keys-1]->userid));
  345.            first_userid = FALSE;
  346.         }
  347.      case CTB_TRUST:
  348.      case CTB_SIG:
  349.         break;
  350.      default:
  351.         error(ERR_NOT_A_KEYRING);
  352.       }
  353.       fseek(ifp, ctbpos = fpos += packet_length, SEEK_SET);
  354.       count = fread(&ctb, 1, 1, ifp);
  355.    }
  356.    key[keys-1]->length = ctbpos - key[keys-1]->fpos;
  357.    fclose(ifp);
  358.  
  359.    /* Sort keydata in main memory */
  360.    if (sort_criterion != SORT_NOSORT)
  361.       sort_keydata(key,0,keys-1);
  362.  
  363.    /* Backup the unsorted keyring in case something goes wrong */
  364.    strcpy(bakring,keyring);
  365.    force_extension(bakring,".bak");
  366.    remove(bakring);
  367.    if (rename(keyring,bakring))
  368.       error(ERR_COULD_NOT_BACKUP);
  369.  
  370.    /* Copy the keys from old to new keyring in sorted order */
  371.    if (!(ifp=fopen(bakring,"rb")))
  372.       error(ERR_COULD_NOT_BACKUP);
  373.    if (!(ofp=fopen(keyring,"wb")))
  374.       error(ERR_COULD_NOT_BACKUP);
  375.    for (i=0; i<keys; i++)
  376.       if (!key[i]->removed) {
  377.          fseek(ifp,key[i]->fpos, SEEK_SET);
  378.          while (key[i]->length > 0) {
  379.         size_t bytes = MIN(key[i]->length, sizeof(buf));
  380.         fread(&buf, bytes, 1, ifp);
  381.         fwrite(&buf, bytes, 1, ofp);
  382.         key[i]->length -= bytes;
  383.      }
  384.       }
  385.    fclose(ofp);
  386.  
  387. end:
  388.    /* Remember to cleanup after us */
  389.    fclose(ifp);
  390.    for (i = 0; i < keys; i++)
  391.       free(key[i]);
  392.    return status;
  393. }
  394.  
  395. void show_usage()
  396. {
  397.    fprintf(stderr, "\nPGPSort v1.01  (c) 1995 stale@hypnotech.com\n\n");
  398.    fprintf(stderr, "Synopsis: Sorts PGP 2.x keyrings\n\n");
  399.    fprintf(stderr, "Usage   : pgpsort +u[r] [keyring] - sort on user ID\n");
  400.    fprintf(stderr, "          pgpsort +k[r] [keyring] - sort on key ID\n");
  401.    fprintf(stderr, "          pgpsort +s[r] [keyring] - sort on key size\n");
  402.    fprintf(stderr, "          pgpsort +d[r] [keyring] - sort on date of creation\n");
  403.    fprintf(stderr, "          pgpsort -r    [keyring] - remove keys (no sorting)\n\n");
  404.    fprintf(stderr, "          Use 'r' to remove bad keys\n");
  405.    fprintf(stderr, "          Use '-' instead of '+' to sort in descending order\n\n");
  406.    exit(ERR_ILLEGAL_PARAMS);
  407. }
  408.  
  409. int main(int argc, char *argv[])
  410. {
  411.    char *sort_option;
  412.    char filename[FILENAME_MAX];
  413.  
  414.    /* Parse command line options */
  415.    if (argc==2 || argc==3) {
  416.       sort_option = argv[1];
  417.       if (argc==3)
  418.      strcpy(filename, argv[2]);
  419.       else {
  420.      printf("No filename given, assuming default keyring.\n");
  421.      if (getenv("PGPPATH"))
  422.         add_slash(strcpy(filename, getenv("PGPPATH")));
  423.      else if (getenv("HOME") && strchr(getenv("HOME"), '/'))
  424.          strcat(add_slash(strcpy(filename, getenv("HOME"))), ".pgp/");
  425.          else
  426.         filename[0]='\0';
  427.      strcat(filename, "pubring.pgp");
  428.       }
  429.    } else
  430.      show_usage();
  431.    if (strlen(sort_option)==2 || strlen(sort_option)==3) {
  432.       switch (sort_option[0]) {
  433.          case '+': sort_order=ASCENDING;  break;
  434.          case '-': sort_order=DESCENDING; break;
  435.          default : show_usage();
  436.       }
  437.       if (strlen(sort_option)==2)
  438.          remove_bad_keys = FALSE;
  439.       else if (sort_option[1]!='r' && sort_option[2]=='r')
  440.          remove_bad_keys = TRUE;
  441.       else
  442.          show_usage();
  443.       switch (sort_option[1]) {
  444.          case 'u': sort_criterion=SORT_ON_USERID; break;
  445.          case 'k': sort_criterion=SORT_ON_KEYID;  break;
  446.          case 's': sort_criterion=SORT_ON_SIZE;   break;
  447.          case 'd': sort_criterion=SORT_ON_DATE;   break;
  448.          case 'r': sort_criterion=SORT_NOSORT;
  449.                    remove_bad_keys=TRUE;          break;
  450.          default : show_usage();
  451.       }
  452.    } else
  453.       show_usage();
  454.  
  455.    /* OK, attempt to sort the keyring */
  456.    switch (sort_keyring(filename)) {
  457.       case ERR_OK:
  458.      printf("Keyring '%s' ", filename);
  459.      switch (sort_criterion) {
  460.         case SORT_NOSORT   : printf("processed.\n"); break;
  461.         case SORT_ON_USERID: printf("sorted on user ID.\n"); break;
  462.         case SORT_ON_KEYID : printf("sorted on key ID.\n"); break;
  463.         case SORT_ON_SIZE  : printf("sorted on key size.\n"); break;
  464.         case SORT_ON_DATE  : printf("sorted on date.\n");
  465.      }
  466.      return ERR_OK;
  467.       case ERR_FILE_NOT_OPEN:
  468.      fprintf(stderr, "Could not open keyring file '%s'.\n", filename);
  469.      return ERR_FILE_NOT_OPEN;
  470.       case ERR_NOT_A_KEYRING:
  471.      fprintf(stderr, "File '%s' is not a PGP 2.x keyring.\n", filename);
  472.      return ERR_NOT_A_KEYRING;
  473.       case ERR_COULD_NOT_BACKUP:
  474.      fprintf(stderr, "Could not create backup file, keyring not sorted.\n");
  475.      return ERR_COULD_NOT_BACKUP;
  476.       case ERR_KEYRING_CORRUPT:
  477.      fprintf(stderr, "Keyring '%s' is corrupt, cannot sort it.\n", filename);
  478.      return ERR_KEYRING_CORRUPT;
  479.       case ERR_OUT_OF_MEMORY:
  480.      fprintf(stderr, "Out of memory: keyring '%s' is too big to sort.\n", filename);
  481.     return ERR_OUT_OF_MEMORY;
  482.    }
  483.  
  484.    return 0;  /* Just to please some compilers */
  485. }
  486.