Annotation of pgp/contrib/pgpsort/pgpsort.c, revision 1.1.1.1

1.1       root        1:  /******************************************************
                      2:  *                                                     *
                      3:  *     PGPSort: a utility to sort PGP 2.x keyrings     *
                      4:  *               .                                     *
                      5:  *  (c)1995 by Stale Schumacher <[email protected]>  *
                      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 [email protected]\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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.