|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.