|
|
1.1.1.6 root 1: /* config.c - config file parser by Peter Gutmann
2: Parses config file for PGP
1.1.1.7 ! root 3:
1.1.1.6 root 4: (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved.
5: The author assumes no liability for damages resulting from the use
6: of this software, even if the damage results from defects in this
7: software. No warranty is expressed or implied.
1.1.1.7 ! root 8:
1.1.1.6 root 9: Note that while most PGP source modules bear Philip Zimmermann's
10: copyright notice, many of them have been revised or entirely written
11: by contributors who frequently failed to put their names in their
12: code. Code that has been incorporated into PGP from other authors
13: was either originally published in the public domain or is used with
14: permission from the various authors.
1.1.1.7 ! root 15:
1.1.1.6 root 16: PGP is available for free to the public under certain restrictions.
17: See the PGP User's Guide (included in the release package) for
18: important information about licensing, patent restrictions on
19: certain algorithms, trademarks, copyrights, and export controls.
1.1.1.7 ! root 20:
1.1.1.6 root 21: Modified 24 Jun 92 - HAJK
22: Misc fixes for VAX C restrictions.
1.1.1.7 ! root 23:
! 24: */
1.1.1.6 root 25:
26: #include <ctype.h>
27: #include <string.h>
28: #include <stdio.h>
29: #include <stdlib.h>
30: #include "usuals.h"
31: #include "fileio.h"
32: #include "pgp.h"
33: #include "config.h"
34: #include "charset.h"
35:
36: static int lookup( char *key, int keyLength, char *keyWords[], int range );
37: static int extractToken( char *buffer, int *endIndex, int *length );
38: static int getaString( char *buffer, int *endIndex );
1.1.1.7 ! root 39: static int getAssignment( char *buffer, int *endIndex,
! 40: INPUT_TYPE settingType );
1.1.1.6 root 41: static void processAssignment( int intrinsicIndex );
42:
43: /* The external config variables we can set here are referenced in pgp.h */
44:
45: /* Return values */
46:
47: #define ERROR -1
48: #define OK 0
49:
50: /* The types of error we check for */
51:
52: enum { NO_ERROR, ILLEGAL_CHAR_ERROR, LINELENGTH_ERROR };
53:
54: #define CPM_EOF 0x1A /* ^Z = CPM EOF char */
55:
1.1.1.7 ! root 56: #define MAX_ERRORS 3 /* Max.no.errors before we give up */
1.1.1.6 root 57:
58: #define LINEBUF_SIZE 100 /* Size of input buffer */
59:
1.1.1.7 ! root 60: static int line; /* The line on which an error occurred */
1.1.1.6 root 61: static int errCount; /* Total error count */
1.1.1.7 ! root 62: static boolean hasError; /* Whether this line has an error in it */
1.1.1.6 root 63:
64: /* The settings parsed out by getAssignment() */
65:
66: static char str[ LINEBUF_SIZE ];
67: static int value;
68: static boolean flag;
69: static char *errtag; /* prefix for printing error messages */
70: static char optstr[100]; /* option being processed */
71:
72: /* A .CFG file roughly follows the format used in the world-famous HPACK
73: archiver and is as follows:
74:
75: - Leading spaces/tabs (whitespace) are ignored.
76:
1.1.1.7 ! root 77: - Lines with a '#' as the first non-whitespace character are treated
! 78: as comment lines.
1.1.1.6 root 79:
80: - All other lines are treated as config options for the program.
81:
82: - Lines may be terminated by either linefeeds, carriage returns, or
83: carriage return/linefeed pairs (the latter being the DOS default
84: method of storing text files).
85:
86: - Config options have the form:
87:
88: <option> '=' <setting>
89:
90: where <setting> may be 'on', 'off', a numeric value, or a string
91: value.
92:
93: - If strings have spaces or the '#' character inside them they must be
94: surrounded by quote marks '"' */
95:
96: /* Intrinsic variables */
97:
98: #define NO_INTRINSICS (sizeof(intrinsics) / sizeof(intrinsics[0]))
99:
100: enum
101: { ARMOR, COMPRESS, SHOWPASS, KEEPBINARY, LANGUAGE,
102: MYNAME, TEXTMODE, TMP, TZFIX, VERBOSE, BAKRING,
103: ARMORLINES, COMPLETES_NEEDED, MARGINALS_NEEDED, PAGER,
104: CERT_DEPTH, CHARSET, CLEAR, SELF_ENCRYPT,
105: INTERACTIVE, PKCS_COMPAT, PUBRING, SECRING, RANDSEED,
106: COMMENT,
107: /* options below this line can only be used as command line
108: * "long" options */
109: #define CONFIG_INTRINSICS BATCHMODE
110: BATCHMODE, FORCE, LEGAL_KLUDGE, NOMANUAL
111: };
112:
113: static char *intrinsics[] =
114: { "ARMOR", "COMPRESS", "SHOWPASS", "KEEPBINARY", "LANGUAGE",
115: "MYNAME", "TEXTMODE", "TMP", "TZFIX", "VERBOSE", "BAKRING",
116: "ARMORLINES", "COMPLETES_NEEDED", "MARGINALS_NEEDED", "PAGER",
117: "CERT_DEPTH", "CHARSET", "CLEARSIG", "ENCRYPTTOSELF",
118: "INTERACTIVE", "PKCS_COMPAT", "PUBRING", "SECRING", "RANDSEED",
119: "COMMENT",
120: /* command line only */
121: "BATCHMODE", "FORCE", "LEGAL_KLUDGE", "NOMANUAL"
122: };
123:
124: static INPUT_TYPE intrinsicType[] =
125: { BOOL, BOOL, BOOL, BOOL, STRING,
126: STRING, BOOL, STRING, NUMERIC, NUMERIC, STRING,
127: NUMERIC, NUMERIC, NUMERIC, STRING,
128: NUMERIC, STRING, BOOL, BOOL,
129: BOOL, NUMERIC, STRING, STRING, STRING,
130: STRING,
131: /* command line only */
132: BOOL, BOOL, BOOL, BOOL
133: };
134:
135: /* Possible settings for variables */
136:
137: #define NO_SETTINGS 2
138:
139: static char *settings[] = { "OFF", "ON" };
140:
141:
142: /* Search a list of keywords for a match */
143: static int lookup( char *key, int keyLength, char *keyWords[], int range )
144: {
145: int indx, pos = 0, matches = 0;
146:
147: strncpy(optstr, key, keyLength);
148: optstr[keyLength] = '\0';
149: /* Make the search case insensitive */
150: for( indx = 0; indx < keyLength; indx++ )
151: key[ indx ] = to_upper( key[ indx ] );
152:
153: for( indx = 0; indx < range; indx++ )
154: if( !strncmp( key, keyWords[ indx ], keyLength ) ) {
155: if (strlen(keyWords[indx]) == keyLength)
156: return indx; /* exact match */
157: pos = indx;
158: ++matches;
159: }
160:
161: switch (matches) {
162: case 0:
1.1.1.7 ! root 163: fprintf(stderr, "%s: unknown keyword: \"%s\"\n",
! 164: errtag, optstr);
1.1.1.6 root 165: break;
166: case 1:
167: return pos;
168: default:
1.1.1.7 ! root 169: fprintf(stderr, "%s: \"%s\" is ambiguous\n",
! 170: errtag, optstr);
1.1.1.6 root 171: }
172: return ERROR;
173: }
174:
175: /* Extract a token from a buffer */
176: static int extractToken( char *buffer, int *endIndex, int *length )
177: {
178: int indx = 0, tokenStart;
179: char ch;
180:
181: /* Skip whitespace */
1.1.1.7 ! root 182: for( ch = buffer[ indx ]; ch && ( ch == ' ' || ch == '\t' );
! 183: ch = buffer[ indx ] )
1.1.1.6 root 184: indx++;
185: tokenStart = indx;
186:
187: /* Find end of setting */
1.1.1.7 ! root 188: while( indx < LINEBUF_SIZE && ( ch = buffer[ indx ] ) != '\0'
! 189: && ch != ' ' && ch != '\t' )
1.1.1.6 root 190: indx++;
191: *endIndex += indx;
192: *length = indx - tokenStart;
193:
194: /* Return start position of token in buffer */
195: return tokenStart;
196: }
197:
198:
199: /* Get a string constant */
200: static int getaString( char *buffer, int *endIndex )
201: {
202: boolean noQuote = FALSE;
203: int stringIndex = 0, bufIndex = 1;
204: char ch = *buffer;
205:
206: /* Skip whitespace */
207: while( ch && ( ch == ' ' || ch == '\t' ) )
208: ch = buffer[ bufIndex++ ];
209:
210: /* Check for non-string */
211: if( ch != '\"' ) {
212: *endIndex += bufIndex;
213:
214: /* Check for special case of null string */
215: if( !ch ) {
216: *str = '\0';
217: return OK;
218: }
219:
220: /* Use nasty non-rigorous string format */
221: noQuote = TRUE;
222: }
223:
224: /* Get first char of string */
225: if( !noQuote )
226: ch = buffer[ bufIndex++ ];
227:
228: /* Get string into string */
229: while( ch && ch != '\"' ) {
230: /* Exit on '#' if using non-rigorous format */
231: if( noQuote && ch == '#' )
232: break;
233:
234: str[ stringIndex++ ] = ch;
235: ch = buffer[ bufIndex++ ];
236: }
237:
238: /* If using the non-rigorous format, stomp trailing spaces */
239: if( noQuote )
240: while( stringIndex > 0 && str[ stringIndex - 1 ] == ' ' )
241: stringIndex--;
242:
243: str[ stringIndex++ ] = '\0';
244: *endIndex += bufIndex;
245:
246: /* Check for missing string terminator */
247: if( ch != '\"' && !noQuote ) {
248: if (line)
1.1.1.7 ! root 249: fprintf(stderr,
! 250: "%s: unterminated string in line %d\n",
! 251: errtag, line );
1.1.1.6 root 252: else
253: fprintf(stderr, "unterminated string: '\"%s'\n", str );
254: hasError = TRUE;
255: errCount++;
256: return ERROR;
257: }
258:
259: return OK;
260: }
261:
262: /* Get an assignment to an intrinsic */
263: static int getAssignment( char *buffer, int *endIndex, INPUT_TYPE settingType )
264: {
265: int settingIndex = 0, length;
266:
267: buffer += extractToken( buffer, endIndex, &length );
268:
269: /* Check for an assignment operator */
270: if ( *buffer != '=' ) {
271: if (line)
1.1.1.7 ! root 272: fprintf(stderr,
! 273: "%s: expected '=' in line %d\n",
! 274: errtag, line );
1.1.1.6 root 275: else
1.1.1.7 ! root 276: fprintf(stderr,
! 277: "%s: expected '=' after \"%s\"\n",
! 278: errtag, optstr);
1.1.1.6 root 279: hasError = TRUE;
280: errCount++;
281: return ERROR;
282: }
283: buffer++; /* Skip '=' */
284:
285: buffer += extractToken( buffer, endIndex, &length );
286:
287: switch( settingType ) {
288: case BOOL:
289: /* Check for known intrinsic - really more general than just
290: checking for TRUE or FALSE */
1.1.1.7 ! root 291: if( ( settingIndex = lookup( buffer,
! 292: length, settings,
! 293: NO_SETTINGS ) ) == ERROR )
1.1.1.6 root 294: {
295: hasError = TRUE;
296: errCount++;
297: return ERROR;
298: }
299:
300: flag = ( settingIndex == 0 ) ? FALSE : TRUE;
301: break;
302:
303: case STRING:
304: /* Get a string */
305: getaString( buffer, &length );
306: break;
307:
308: case NUMERIC:
309: /* Get numeric input. Error checking is a pain since atoi()
310: has no real equivalent of NAN */
311: value = atoi( buffer );
312: break;
313: }
314:
315: return settingIndex;
316: }
317:
318: /* Process an assignment */
319: static void processAssignment( int intrinsicIndex )
320: {
321: if( !hasError )
322: switch( intrinsicIndex ) {
323: case ARMOR:
324: emit_radix_64 = flag;
325: break;
326:
327: case COMPRESS:
328: compress_enabled = flag;
329: break;
330:
331: case SHOWPASS:
332: showpass = flag;
333: break;
334:
335: case KEEPBINARY:
336: keepctx = flag;
337: break;
338:
339: case LANGUAGE:
340: strncpy(language, str, 15);
341: break;
342:
343: case BAKRING:
344: strcpy(floppyring, str);
345: break;
346:
347: case MYNAME:
348: strcpy(my_name, str);
349: break;
350:
351: case TEXTMODE:
352: if( flag )
353: literal_mode = MODE_TEXT;
354: else
355: literal_mode = MODE_BINARY;
356: break;
357:
358: case TMP:
359: /* directory pathname to store temp files */
360: settmpdir(str);
361: break;
362:
363: case TZFIX:
364: /* How many hours to add to time() to get GMT. */
365: /* Compute seconds from hours to shift to GMT: */
366: timeshift = 3600L * (long) value;
367: break;
368:
369: case VERBOSE:
370: if (value < 1) {
371: quietmode = TRUE; verbose = FALSE;
372: } else if (value == 1) {
373: quietmode = FALSE; verbose = FALSE;
374: } else { /* value > 1 */
375: quietmode = FALSE; verbose = TRUE;
376: }
377: break;
378:
379: case ARMORLINES:
380: pem_lines = value;
381: break;
382:
383: case MARGINALS_NEEDED:
384: marg_min = value;
385: if (marg_min < 1)
386: marg_min = 1;
387: break;
388:
389: case COMPLETES_NEEDED:
390: compl_min = value;
391: if (compl_min < 1)
392: compl_min = 1;
393: if (compl_min > 4)
394: compl_min = 4;
395: break;
396:
397: case CERT_DEPTH:
398: max_cert_depth = value;
399: if (max_cert_depth < 0)
400: max_cert_depth = 0;
401: if (max_cert_depth > 8)
402: max_cert_depth = 8;
403: break;
404:
405: case PAGER:
406: strcpy(pager, str);
407: break;
408:
409: case CHARSET:
410: strcpy(charset, str);
411: break;
412:
413: case CLEAR:
414: clear_signatures = flag;
415: break;
416:
417: case SELF_ENCRYPT:
418: encrypt_to_self = flag;
419: break;
420:
421: case INTERACTIVE:
422: interactive_add = flag;
423: break;
424:
425: case BATCHMODE: batchmode = flag; break;
426: case FORCE: force_flag = flag; break;
427: case PKCS_COMPAT: pkcs_compat = value; break;
428:
1.1.1.7 ! root 429: /* Undocumented flag to test contractually-mandated time bomb */
1.1.1.6 root 430: case LEGAL_KLUDGE:
431: legal_kludge = value;
432: break;
433:
434: case NOMANUAL: nomanual = value; break;
435:
436: case PUBRING:
437: strcpy(globalPubringName, str);
438: break;
439:
440: case SECRING:
441: strcpy(globalSecringName, str);
442: break;
443:
444: case RANDSEED:
445: strcpy(globalRandseedName, str);
446: break;
447:
448: case COMMENT:
449: strcpy(globalCommentString, str);
450: break;
451: }
452: }
453:
454: /* Process an option on a line by itself. This expects options which are
455: taken from the command-line, and is less finicky about errors than the
456: config-file version */
457: int processConfigLine( char *option )
458: {
459: int indx, intrinsicIndex;
460: char ch;
461:
462: /* Give it a pseudo-linenumber of 0 */
463: line = 0;
464:
465: errtag = "pgp";
466: errCount = 0;
467: for( indx = 0;
468: indx < LINEBUF_SIZE && ( ch = option[ indx ] ) != '\0' &&
469: ch != ' ' && ch != '\t' && ch != '=';
470: indx++ );
1.1.1.7 ! root 471: if( ( intrinsicIndex = lookup( ( char * ) option,
! 472: indx, intrinsics,
! 473: NO_INTRINSICS ) ) == ERROR )
1.1.1.6 root 474: return -1;
475: if (option[indx] == '\0' && intrinsicType[intrinsicIndex] == BOOL)
476: {
477: /* boolean option, no '=' means TRUE */
478: flag = TRUE;
479: processAssignment(intrinsicIndex);
480: } else /* Get the value to set to, either as a string, a
481: numeric value, or a boolean flag */
1.1.1.7 ! root 482: if (getAssignment( ( char * ) option + indx,
! 483: &indx, intrinsicType[ intrinsicIndex ] )
! 484: != ERROR)
1.1.1.6 root 485: processAssignment( intrinsicIndex );
486: return errCount ? -1 : 0;
487: }
488:
489: /* Process a config file */
490: int processConfigFile( char *configFileName )
491: {
492: FILE *configFilePtr;
493: int ch = 0, theCh;
494: int errType, errPos = 0, lineBufCount, intrinsicIndex;
495: int indx;
496: char inBuffer[ LINEBUF_SIZE ];
497:
498: line = 1;
499: errCount = 0;
500: errtag = "config.txt";
501:
502: if( ( configFilePtr = fopen( configFileName, FOPRTXT ) ) == NULL ) {
1.1.1.7 ! root 503: fprintf(stderr, "Cannot open configuration file %s\n",
! 504: configFileName );
1.1.1.6 root 505: return OK; /* treat like empty config file */
506: }
507:
508: /* Process each line in the configFile */
509: while( ch != EOF ) {
510: /* Skip whitespace */
1.1.1.7 ! root 511: while( ( ( ch = getc( configFilePtr ) ) == ' ' || ch == '\t' )
! 512: && ch != EOF )
1.1.1.6 root 513: ;
514:
515: /* Get a line into the inBuffer */
516: hasError = FALSE;
517: lineBufCount = 0;
518: errType = NO_ERROR;
519: while( ch != '\r' && ch != '\n' && ch != CPM_EOF && ch != EOF )
520: {
521: /* Check for an illegal char in the data */
1.1.1.7 ! root 522: if( ( ch < ' ' || ch > '~' ) &&
! 523: ch != '\r' && ch != '\n' &&
! 524: ch != ' ' && ch != '\t' && ch != CPM_EOF
! 525: && ch != EOF )
1.1.1.6 root 526: {
527: if( errType == NO_ERROR )
1.1.1.7 ! root 528: /* Save position of first illegal char */
1.1.1.6 root 529: errPos = lineBufCount;
530: errType = ILLEGAL_CHAR_ERROR;
531: }
532:
1.1.1.7 ! root 533: /* Make sure the path is of the correct
! 534: length. Note that the code is ordered
! 535: so that a LINELENGTH_ERROR takes
! 536: precedence over an ILLEGAL_CHAR_ERROR */
! 537:
1.1.1.6 root 538: if( lineBufCount > LINEBUF_SIZE )
539: errType = LINELENGTH_ERROR;
540: else
541: inBuffer[ lineBufCount++ ] = ch;
542:
543: if( ( ch = getc( configFilePtr ) ) == '#' )
544: {
1.1.1.7 ! root 545: /* Skip comment section and trailing
! 546: whitespace */
! 547: while( ch != '\r' && ch != '\n'
! 548: && ch != CPM_EOF && ch != EOF )
! 549: ch = getc( configFilePtr );
1.1.1.6 root 550: break;
551: }
552: }
553:
554: /* Skip trailing whitespace and add der terminador */
1.1.1.7 ! root 555: while(lineBufCount &&
! 556: (( theCh = inBuffer[ lineBufCount - 1 ] )
! 557: == ' ' || theCh == '\t' ))
! 558: lineBufCount--;
1.1.1.6 root 559:
560: inBuffer[ lineBufCount ] = '\0';
561:
562: /* Process the line unless its a blank or comment line */
563: if( lineBufCount && *inBuffer != '#' )
564: {
565: switch( errType )
566: {
567: case LINELENGTH_ERROR:
1.1.1.7 ! root 568: fprintf(stderr,
! 569: "%s: line '%.30s...' too long\n",
! 570: errtag, inBuffer );
1.1.1.6 root 571: errCount++;
572: break;
573:
574: case ILLEGAL_CHAR_ERROR:
575: fprintf(stderr, "> %s\n ", inBuffer );
576: fprintf(stderr, "%*s^\n", errPos, "");
1.1.1.7 ! root 577: fprintf(stderr,
! 578: "%s: bad character in command on line %d\n",
! 579: errtag, line );
1.1.1.6 root 580: errCount++;
581: break;
582:
583: default:
584: for( indx = 0;
1.1.1.7 ! root 585: indx < LINEBUF_SIZE &&
! 586: ( ch = inBuffer[ indx ] ) != '\0'
! 587: && ch != ' ' && ch != '\t' && ch != '=';
! 588: indx++ );
! 589:
! 590: if( ( intrinsicIndex =
! 591: lookup( inBuffer, indx, intrinsics,
! 592: CONFIG_INTRINSICS ) ) == ERROR )
1.1.1.6 root 593: {
594: errCount++;
595: } else {
1.1.1.7 ! root 596: /* Get the value to set to, either as a
! 597: string, a numeric value, or a boolean
! 598: flag */
! 599: getAssignment(inBuffer + indx,
! 600: &indx,
! 601: intrinsicType[intrinsicIndex]);
! 602: processAssignment( intrinsicIndex );
1.1.1.6 root 603: }
604: }
605: }
606:
1.1.1.7 ! root 607: /* Handle special-case of ^Z if configFile came off an MSDOS system */
1.1.1.6 root 608: if( ch == CPM_EOF )
609: ch = EOF;
610:
611: /* Exit if there are too many errors */
612: if( errCount >= MAX_ERRORS )
613: break;
614:
615: line++;
616: }
617:
618: fclose( configFilePtr );
619:
620: /* Exit if there were errors */
621: if( errCount ) {
1.1.1.7 ! root 622: fprintf(stderr,
! 623: "%s: %s%d error(s) detected\n\n",
! 624: configFileName, ( errCount >= MAX_ERRORS ) ?
! 625: "Maximum level of " : "", errCount );
1.1.1.6 root 626: return ERROR;
627: }
628:
629: return OK;
630: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.