1 /* $NetBSD: parser.c,v 1.5 2015/08/08 12:34:33 shm Exp $ */ 2 3 /* Copyright (c) 2010 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Mateusz Kocielski. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the NetBSD 20 * Foundation, Inc. and its contributors. 21 * 4. Neither the name of The NetBSD Foundation nor the names of its 22 * contributors may be used to endorse or promote products derived 23 * from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 #include <sys/cdefs.h> 38 __RCSID("$NetBSD: parser.c,v 1.5 2015/08/08 12:34:33 shm Exp $"); 39 40 #include <sys/stat.h> 41 #include <sys/syslimits.h> /* for PATH_MAX */ 42 43 #include <ctype.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <saslc.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 51 #include "dict.h" 52 #include "msg.h" 53 #include "parser.h" 54 #include "saslc_private.h" 55 56 #define SASLC__COMMENT_CHAR '#' 57 58 /* config file location defines */ 59 #define SASLC__CONFIG_PATH "/etc/saslc.d" 60 #define SASLC__CONFIG_MAIN_FILE "saslc" 61 #define SASLC__CONFIG_MECH_DIRECTORY "mech" 62 #define SASLC__CONFIG_SUFFIX ".conf" 63 #define SASLC__DEFAULT_APPNAME "saslc" 64 65 /* token types */ 66 enum { 67 TOKEN_KEY, /* option (key) */ 68 TOKEN_STRING, /* quoted string */ 69 TOKEN_NUM, /* number */ 70 TOKEN_COMMENT, /* comment character */ 71 TOKEN_UNKNOWN /* unknown */ 72 }; 73 74 /* token structure */ 75 typedef struct saslc__token_t { 76 int type; /**< token type */ 77 char *val; /**< token string value */ 78 } saslc__token_t; 79 80 static inline char * skip_WS(char * p)81 skip_WS(char *p) 82 { 83 84 while (*p == ' ' || *p == '\t') 85 p++; 86 return p; 87 } 88 89 /** 90 * @brief gets token from string c and updates pointer position. 91 * @param c pointer to string 92 * @return token on success, NULL on failure (e.g. at end of string). 93 * On success, c is updated to point on next token. It's position is 94 * undefined on failure. 95 * 96 * Note: A legal key begins with an isalpha(3) character and is 97 * followed by isalnum(3) or '_' characters. 98 */ 99 static saslc__token_t * saslc__parse_get_token(char ** c)100 saslc__parse_get_token(char **c) 101 { 102 saslc__token_t *token; 103 char *e; 104 105 *c = skip_WS(*c); 106 if (**c == '\0') 107 return NULL; 108 109 if ((token = calloc(1, sizeof(*token))) == NULL) 110 return NULL; 111 112 token->val = *c; 113 114 if (**c == SASLC__COMMENT_CHAR) 115 token->type = TOKEN_COMMENT; 116 117 else if (**c == '\"') 118 token->type = TOKEN_STRING; 119 120 else if (isdigit((unsigned char)**c)) 121 token->type = TOKEN_NUM; 122 123 else if (isalpha((unsigned char)**c)) 124 token->type = TOKEN_KEY; 125 126 else 127 token->type = TOKEN_UNKNOWN; 128 129 switch (token->type) { 130 case TOKEN_COMMENT: 131 break; 132 case TOKEN_NUM: 133 errno = 0; 134 (void)strtoll(*c, &e, 0); 135 if (errno != 0) 136 goto err; 137 *c = e; 138 break; 139 case TOKEN_KEY: 140 (*c)++; 141 while (isalnum((unsigned char)**c) || **c == '_') 142 (*c)++; 143 break; 144 case TOKEN_STRING: 145 token->val++; /* skip initial '\"' */ 146 (*c)++; 147 /* 148 * XXX: should we allow escapes inside the string? 149 */ 150 while (**c != '\0' && **c != '\"') 151 (*c)++; 152 if (**c != '\"') 153 goto err; 154 **c = '\0'; /* kill trailing '\"' */ 155 (*c)++; 156 break; 157 case TOKEN_UNKNOWN: 158 goto err; 159 } 160 161 if (isspace((unsigned char)**c)) 162 *(*c)++ = '\0'; 163 else if (**c == SASLC__COMMENT_CHAR) 164 **c = '\0'; 165 else if (**c != '\0') 166 goto err; 167 168 return token; 169 err: 170 free(token); 171 return NULL; 172 } 173 174 /** 175 * @brief parses line and store result in dict. 176 * @param line input line 177 * @param dict dictionary in which parsed options will be stored 178 * @return 0 on success, -1 on failure. 179 */ 180 static int saslc__parse_line(char * line,saslc__dict_t * dict)181 saslc__parse_line(char *line, saslc__dict_t *dict) 182 { 183 saslc__dict_result_t rv; 184 saslc__token_t *t; 185 char *key; 186 187 key = NULL; 188 while ((t = saslc__parse_get_token(&line)) != NULL) { 189 if (t->type == TOKEN_COMMENT) { 190 free(t); 191 break; 192 } 193 194 if (key == NULL) { /* get the key */ 195 if (t->type != TOKEN_KEY) 196 goto err; 197 key = t->val; 198 } 199 else { /* get the value and insert in dictionary */ 200 if (t->type != TOKEN_STRING && t->type != TOKEN_NUM) 201 goto err; 202 rv = saslc__dict_insert(dict, key, t->val); 203 if (rv != DICT_OK && rv != DICT_KEYEXISTS) 204 goto err; 205 key = NULL; 206 } 207 free(t); 208 } 209 if (*line != '\0') /* processed entire line */ 210 return -1; 211 if (key != NULL) /* completed key/value cycle */ 212 return -1; 213 return 0; 214 err: 215 free(t); 216 return -1; 217 } 218 219 /** 220 * @brief parses file and store result in dict 221 * @param ctx saslc context 222 * @param path path to the file 223 * @param dict dictionary in which parsed options will be stored 224 * @return 0 on success, -1 on failure. 225 */ 226 static int saslc__parse_file(saslc_t * ctx,char * path,saslc__dict_t * dict)227 saslc__parse_file(saslc_t *ctx, char *path, saslc__dict_t *dict) 228 { 229 FILE *fp; 230 char *buf, *lbuf; 231 size_t len; 232 int rv; 233 234 if ((fp = fopen(path, "r")) == NULL) { 235 /* Don't fail if we can't open the file. */ 236 saslc__msg_dbg("%s: fopen: %s: %s", __func__, path, 237 strerror(errno)); 238 return 0; 239 } 240 saslc__msg_dbg("%s: parsing: \"%s\"", __func__, path); 241 rv = 0; 242 lbuf = NULL; 243 while ((buf = fgetln(fp, &len)) != NULL) { 244 if (buf[len - 1] == '\n') 245 buf[len - 1] = '\0'; 246 else { 247 if ((lbuf = malloc(len + 1)) == NULL) { 248 saslc__error_set(ERR(ctx), ERROR_NOMEM, NULL); 249 rv = -1; 250 break; 251 } 252 memcpy(lbuf, buf, len); 253 lbuf[len] = '\0'; 254 buf = lbuf; 255 } 256 if (saslc__parse_line(buf, dict) == -1) { 257 saslc__error_set(ERR(ctx), ERROR_PARSE, 258 "can't parse file"); 259 rv = -1; 260 break; 261 } 262 if (lbuf != NULL) { 263 free(lbuf); 264 lbuf = NULL; 265 } 266 } 267 if (lbuf != NULL) 268 free(lbuf); 269 270 fclose(fp); 271 return rv; 272 } 273 274 /** 275 * @brief determine if a string indicates true or not. 276 * @return true if the string is "true", "yes", or any nonzero 277 * integer; false otherwise. 278 * 279 * XXX: does this really belong here? Used in parser.c and xsess.c. 280 */ 281 bool saslc__parser_is_true(const char * str)282 saslc__parser_is_true(const char *str) 283 { 284 static const char *true_str[] = { 285 "true", 286 "yes" 287 }; 288 char *e; 289 size_t i; 290 long int val; 291 292 if (str == NULL) 293 return false; 294 295 val = strtol(str, &e, 0); 296 if (*str != '\0' && *e == '\0') 297 return val != 0; 298 299 for (i = 0; i < __arraycount(true_str); i++) 300 if (strcasecmp(str, true_str[i]) == 0) 301 return true; 302 303 return false; 304 } 305 306 /** 307 * @brief parse configuration files. By default function reads 308 * files from /etc/saslc.d/saslc/ directory if appname is not setup. Otherwise 309 * function uses /etc/saslc.d/[appname]/ directory. /etc/saslc.d/ is default 310 * directory which stores configuration for all applications, but can be 311 * overwritten by SASLC_CONFIG variable in environment. 312 * @param ctx saslc context 313 * @return 0 on success, -1 on failure. 314 */ 315 int saslc__parser_config(saslc_t * ctx)316 saslc__parser_config(saslc_t *ctx) 317 { 318 char path[PATH_MAX + 1]; 319 struct stat sb; 320 saslc__mech_list_node_t *mech_node; 321 const char *config_path, *debug, *appname; 322 323 config_path = ctx->pathname; 324 if (config_path == NULL) 325 config_path = getenv(SASLC_ENV_CONFIG); 326 if (config_path == NULL) 327 config_path = SASLC__CONFIG_PATH; 328 329 if (stat(config_path, &sb) == -1 || !S_ISDIR(sb.st_mode)) { 330 /* XXX: should this be fatal or silently ignored? */ 331 saslc__msg_err("%s: stat: config_path='%s': %s", __func__, 332 config_path, strerror(errno)); 333 return 0; 334 } 335 336 if ((appname = ctx->appname) == NULL) 337 appname = SASLC__DEFAULT_APPNAME; 338 339 /* parse global config file */ 340 snprintf(path, sizeof(path), "%s/%s/%s%s", config_path, 341 appname, SASLC__CONFIG_MAIN_FILE, SASLC__CONFIG_SUFFIX); 342 if (saslc__parse_file(ctx, path, ctx->prop) == -1) 343 return -1; 344 345 /* XXX: check this as early as possible! */ 346 debug = saslc__dict_get(ctx->prop, SASLC_PROP_DEBUG); 347 if (debug != NULL) 348 saslc_debug = saslc__parser_is_true(debug); 349 350 /* parse mechanism config files */ 351 LIST_FOREACH(mech_node, ctx->mechanisms, nodes) { 352 snprintf(path, sizeof(path), "%s/%s/%s/%s%s", 353 config_path, appname, SASLC__CONFIG_MECH_DIRECTORY, 354 mech_node->mech->name, SASLC__CONFIG_SUFFIX); 355 if (saslc__parse_file(ctx, path, mech_node->prop) == -1) 356 return -1; 357 } 358 359 return 0; 360 } 361