config.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /*
  2. * config.c
  3. * Functions for the manipulation of configuration files.
  4. *
  5. * Matej Pfajfar <mp292@cam.ac.uk>
  6. */
  7. /*
  8. * Changes :
  9. * $Log$
  10. * Revision 1.2 2002/06/28 18:14:55 montrose
  11. * Added poptReadOptions() and poptReadDefaultOptions()
  12. *
  13. * Revision 1.1.1.1 2002/06/26 22:45:50 arma
  14. * initial commit: current code
  15. *
  16. * Revision 1.7 2002/04/02 14:27:11 badbytes
  17. * Final finishes.
  18. *
  19. * Revision 1.6 2002/01/27 19:23:03 mp292
  20. * Fixed a bug in parameter checking.
  21. *
  22. * Revision 1.5 2002/01/26 18:42:15 mp292
  23. * Reviewed according to Secure-Programs-HOWTO.
  24. *
  25. * Revision 1.4 2002/01/21 21:07:56 mp292
  26. * Parameter checking was missing in some functions.
  27. *
  28. * Revision 1.3 2001/12/07 09:38:03 badbytes
  29. * Tested.
  30. *
  31. * Revision 1.2 2001/12/06 15:43:50 badbytes
  32. * config.c compiles. Proceeding to test it.
  33. *
  34. * Revision 1.1 2001/11/22 01:20:27 mp292
  35. * Functions for dealing with configuration files.
  36. *
  37. *
  38. */
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <errno.h>
  43. #include <ctype.h>
  44. #include <popt.h>
  45. #include <limits.h>
  46. #include "config.h"
  47. #include "log.h"
  48. /* open configuration file for reading */
  49. FILE *open_config(const unsigned char *filename)
  50. {
  51. FILE *f;
  52. if (filename) /* non-NULL filename */
  53. {
  54. if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) == strlen(filename)) /* filename consists of legal characters only */
  55. {
  56. f = fopen(filename, "r");
  57. return f;
  58. } /* filename consists of legal characters only */
  59. else /* illegal values in filename */
  60. {
  61. return NULL;
  62. } /* illegal values in filename */
  63. } /* non-NULL filename */
  64. else /* NULL filename */
  65. return NULL;
  66. }
  67. /* close configuration file */
  68. int close_config(FILE *f)
  69. {
  70. int retval = 0;
  71. if (f) /* valid file descriptor */
  72. {
  73. retval = fclose(f);
  74. return retval;
  75. } /* valid file descriptor */
  76. else
  77. return -1;
  78. }
  79. /* parse the config file and obtain the required option values */
  80. int parse_config(FILE *f, config_opt_t *option)
  81. {
  82. unsigned char keyword[CONFIG_KEYWORD_MAXLEN+1]; /* for storing the option keyword */
  83. unsigned char *buffer = NULL; /* option value */
  84. size_t buflen = 0;
  85. char *errtest = NULL; /* used for testing correctness of strtol() etc. */
  86. unsigned int i_keyword = 0; /* current position within keyword */
  87. unsigned int i_buf = 0; /* current position within buffer */
  88. char c=0; /* input char */
  89. unsigned int state=0; /* internal state
  90. * 0 - trying to find a keyword
  91. * 1 - reading a keyword
  92. * 2 - keyword read and recognized, looking for the option value
  93. * 3 - reading the option value
  94. * 4 - option value read
  95. * 5 - inside a comment
  96. */
  97. int retval=0; /* return value */
  98. int lineno=1; /* current line number */
  99. int curopt=-1; /* current option, as an indexed in config_opt_t */
  100. int i;
  101. if ( (f==NULL) || (option==NULL) ) /* invalid parameters */
  102. return -1;
  103. fseek(f,0,SEEK_SET); /* make sure we start at the beginning of file */
  104. for (;;) /* infinite loop */
  105. {
  106. c = getc(f);
  107. if ((c == '\n') || (c == EOF))
  108. {
  109. if (state == 1) /* reading a keyboard */
  110. {
  111. log(LOG_ERR,"Error parsing the configuration file on line %d.", lineno);
  112. i_keyword = 0;
  113. state = 0;
  114. retval = -1;
  115. break;
  116. } /* reading a keyboard */
  117. else if (state == 2) /* keyword read and recognized */
  118. {
  119. log(LOG_ERR,"Error parsing option %s on line %d.",option[curopt].keyword, lineno);
  120. i_keyword = 0;
  121. state = 0;
  122. option[curopt].err=-1;
  123. retval = -1;
  124. break;
  125. } /* keyboard read and recognized */
  126. else if (state == 3) /* reading the option value */
  127. {
  128. buffer[i_buf++] = 0; /* add NULL character to terminate the string */
  129. state = 4;
  130. /* conversion and copying the value into config_opt_t is done later on */
  131. } /* reading the option value */
  132. else if (state == 5) /* reached end of comment */
  133. state = 0;
  134. if (c == EOF)
  135. {
  136. log(LOG_DEBUG,"parse_config() : Reached eof on line %d.",lineno);
  137. break;
  138. }
  139. else
  140. {
  141. log(LOG_DEBUG,"parse_config() : Reached eol on line %d.", lineno);
  142. lineno++;
  143. }
  144. }
  145. else if ( (state==0) && (c == '#') ) /* lines beginning with # are ignored */
  146. {
  147. log(LOG_DEBUG,"parse_config() : Line %d begins with #.",lineno);
  148. state = 5;
  149. }
  150. else if ( (state==0) && (isspace(c)) ) /* leading whitespace is ignored */
  151. ;
  152. else if ( (state==1) && (isspace(c)) ) /* have apparently read in all of the keyword */
  153. {
  154. keyword[i_keyword++] = 0;
  155. curopt = -1;
  156. for (i=0;option[i].keyword != NULL;i++) /* try and identify the keyword */
  157. {
  158. if (!strncmp(keyword,option[i].keyword,CONFIG_KEYWORD_MAXLEN))
  159. {
  160. curopt = i;
  161. break;
  162. }
  163. } /* try and identify the keyword */
  164. if (curopt == -1) /* can't recognise the keyword */
  165. {
  166. log(LOG_ERR,"Error parsing the configuration file. Cannot recognize keyword %s on line %d.",keyword,lineno);
  167. retval=-1;
  168. break;
  169. }
  170. else
  171. state = 2;
  172. }
  173. else if ( (state==2) && (isspace(c)) ) /* whitespace separating keyword and value is ignored */
  174. ;
  175. else if ( (state==3) && (isspace(c)) ) /* have apparently finished reading the option value */
  176. {
  177. buffer[i_buf++]=0;
  178. state = 4;
  179. }
  180. else /* all other characters */
  181. {
  182. if (state == 0) /* first character of the keyword */
  183. {
  184. log(LOG_DEBUG, "parse_config() : %c is the start of a keyword on line %d.",c,lineno);
  185. state = 1;
  186. i_keyword = 0;
  187. keyword[i_keyword++] = c;
  188. }
  189. else if (state == 1) /* keep on reading the keyword */
  190. {
  191. log(LOG_DEBUG,"parse_config() : %c is a character in the keyword on line %d.",c,lineno);
  192. if (i_keyword < CONFIG_KEYWORD_MAXLEN) /* check for buffer overflow */
  193. keyword[i_keyword++] = c;
  194. else
  195. {
  196. log(LOG_ERR,"Error parsing the configuration file. Keyword on line %d exceeds %d characters.",lineno,CONFIG_KEYWORD_MAXLEN);
  197. retval=-1;
  198. break;
  199. }
  200. }
  201. else if (state == 2) /* first character of the value */
  202. {
  203. log(LOG_DEBUG,"parse_config() : %c is the first character of the option value on line %d.",c,lineno);
  204. state = 3;
  205. i_buf=0;
  206. buflen = CONFIG_VALUE_MAXLEN+1; /* allocate memory for the value buffer */
  207. buffer = (char *)malloc(buflen);
  208. if (!buffer)
  209. {
  210. log(LOG_ERR,"Could not allocate memory.");
  211. retval=-1;
  212. break;
  213. } else
  214. buffer[i_buf++]=c;
  215. }
  216. else if (state == 3) /* keep on reading the value */
  217. {
  218. log(LOG_DEBUG,"parse_config() : %c is a character in the value of the keyword on line %d.",c,lineno);
  219. if (i_buf >= buflen)
  220. {
  221. log(LOG_ERR,"Length of keyword value on line %u exceeds the length limit (%u).",lineno, CONFIG_VALUE_MAXLEN);
  222. retval=-1;
  223. break;
  224. }
  225. buffer[i_buf++]=c;
  226. }
  227. else if (state == 5)
  228. ; /* character is part of a comment, skip */
  229. else /* unexpected error */
  230. {
  231. log(LOG_ERR,"Unexpected error while parsing the configuration file.");
  232. log(LOG_DEBUG,"parse_config() : Encountered a non-delimiter character while not in states 0,1,2 or 3!");
  233. break;
  234. }
  235. }
  236. if (state==4) /* convert the value of the option to the appropriate type and write into OPT */
  237. {
  238. switch(option[curopt].r_type) /* consider each type separately */
  239. {
  240. case CONFIG_TYPE_STRING:
  241. /* resize the buffer to fit the data exactly */
  242. buffer = (char *)realloc(buffer,i_buf);
  243. if (!buffer)
  244. {
  245. log(LOG_ERR,"Could not allocate memory.");
  246. return -1;
  247. }
  248. option[curopt].r.str = buffer;
  249. option[curopt].err = 1;
  250. break;
  251. case CONFIG_TYPE_CHAR:
  252. option[curopt].r.c = *buffer;
  253. option[curopt].err = 1;
  254. break;
  255. case CONFIG_TYPE_INT:
  256. errtest = NULL;
  257. option[curopt].r.i = (int)strtol(buffer,&errtest,0);
  258. if ((unsigned char *)errtest == buffer)
  259. {
  260. log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
  261. option[curopt].err = -1;
  262. if (buffer)
  263. free(buffer);
  264. return -1;
  265. }
  266. else
  267. option[curopt].err = 1;
  268. break;
  269. case CONFIG_TYPE_LONG:
  270. errtest = NULL;
  271. option[curopt].r.l = strtol(buffer,&errtest,0);
  272. if ((unsigned char *)errtest == buffer)
  273. {
  274. log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
  275. option[curopt].err = -1;
  276. if (buffer)
  277. free(buffer);
  278. return -1;
  279. }
  280. else
  281. option[curopt].err = 1;
  282. break;
  283. case CONFIG_TYPE_DOUBLE:
  284. errtest = NULL;
  285. option[curopt].r.d = strtod(buffer,&errtest);
  286. if ((unsigned char *)errtest == buffer)
  287. {
  288. log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
  289. option[curopt].err = -1;
  290. if (buffer)
  291. free(buffer);
  292. return -1;
  293. }
  294. else
  295. option[curopt].err = 1;
  296. break;
  297. default: /* unexpected type */
  298. log(LOG_ERR, "Error parsing configuration file. Unrecognized option type!");
  299. if (buffer)
  300. free(buffer);
  301. return -1;
  302. }
  303. /* clean up */
  304. if (option[curopt].r_type != CONFIG_TYPE_STRING)
  305. {
  306. if (buffer)
  307. free(buffer);
  308. buflen=0;
  309. }
  310. state = 0;
  311. curopt = -1;
  312. i_buf=0;
  313. i_keyword=0;
  314. }
  315. } /* infinite loop */
  316. return retval;
  317. }
  318. int poptReadOptions(poptContext optCon, const unsigned char *fname)
  319. /**
  320. poptReadOptions reads popt-style options from the specified filename.
  321. RETURN VALUE: INT_MIN = problem opening config file, else standard poptGetNextOpt() return value
  322. **/
  323. {
  324. FILE *fp;
  325. int argc, c;
  326. char **argv;
  327. char line[256];
  328. line[0] = line[1] = '-'; /* prepend expected long name option flag */
  329. fp = open_config(fname);
  330. if ( fp == NULL ) return INT_MIN;
  331. c = 0;
  332. /**
  333. this loop skips over all leading whitespace and blank lines then returns all text
  334. from that point to the next newline.
  335. **/
  336. while ( c >= -1 && fscanf(fp,"%*[ \n]%[^\n]",&line[2]) == 1 )
  337. {
  338. switch ( line[2] )
  339. {
  340. case '#': /* comments begin with this */
  341. case '[': /* section header. ignore for now. maybe do something special in future version... */
  342. continue;/* ignore */
  343. default: /* we got a bite, lets reel it in now */
  344. poptParseArgvString(line,&argc,(const char ***)&argv); /* Argv-ify what we found */
  345. poptStuffArgs(optCon,(const char **)argv); /* stuff new arguments so they can be interpreted */
  346. free(argv); /* free storage allocated by poptParseArgvString */
  347. c = poptGetNextOpt(optCon); /* interpret option read from config file */
  348. }
  349. }
  350. close_config(fp);
  351. return c;
  352. }
  353. int poptReadDefaultOptions(const char *cmd, poptContext optCon)
  354. /**
  355. reads popt-style options from /etc/<cmd>rc and ~/.<cmd>rc
  356. RETURN VALUE: same as poptReadOptions()
  357. **/
  358. {
  359. char fname[256];
  360. int c;
  361. sprintf(fname,"/etc/%src",cmd);
  362. c = poptReadOptions(optCon,fname);
  363. if ( c == INT_MIN || c >= -1 )
  364. {
  365. sprintf(fname,"~/.%src",cmd);
  366. c = poptReadOptions(optCon,fname);
  367. }
  368. return c;
  369. }