1/* 2 * Taken from: 3 * WPA Supplicant / Configuration parser and common functions 4 * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> 5 */ 6 7#include <stdlib.h> 8#include <stdio.h> 9#include <errno.h> 10#include <string.h> 11#include <unistd.h> 12 13#include "logging.h" 14#include "config.h" 15 16char * rel2abs_path(const char *rel_path) 17{ 18 char *buf = NULL, *cwd, *ret; 19 size_t len = 128, cwd_len, rel_len, ret_len; 20 int last_errno; 21 22 if (rel_path[0] == '/') 23 return strdup(rel_path); 24 25 for (;;) { 26 buf = malloc(len); 27 if (buf == NULL) 28 return NULL; 29 cwd = getcwd(buf, len); 30 if (cwd == NULL) { 31 last_errno = errno; 32 free(buf); 33 if (last_errno != ERANGE) 34 return NULL; 35 len *= 2; 36 if (len > 2000) 37 return NULL; 38 } else { 39 buf[len - 1] = '\0'; 40 break; 41 } 42 } 43 44 cwd_len = strlen(cwd); 45 rel_len = strlen(rel_path); 46 ret_len = cwd_len + 1 + rel_len + 1; 47 ret = malloc(ret_len); 48 if (ret) { 49 memcpy(ret, cwd, cwd_len); 50 ret[cwd_len] = '/'; 51 memcpy(ret + cwd_len + 1, rel_path, rel_len); 52 ret[ret_len - 1] = '\0'; 53 } 54 free(buf); 55 return ret; 56} 57 58 59struct parse_data { 60 /* Configuration variable name */ 61 char *name; 62 63 /* Parser function for this variable */ 64 int (*parser)(const struct parse_data *data, struct gct_config *config, 65 int line, const char *value); 66 67 /* Variable specific parameters for the parser. */ 68 void *param1, *param2, *param3, *param4; 69 70 /* 0 = this variable can be included in debug output and ctrl_iface 71 * 1 = this variable contains key/private data and it must not be 72 * included in debug output unless explicitly requested. In 73 * addition, this variable will not be readable through the 74 * ctrl_iface. 75 */ 76 int key_data; 77}; 78 79 80static 81char * gct_config_parse_string(const char *value, size_t *len) 82{ 83 if (*value == '"') { 84 const char *pos; 85 char *str; 86 value++; 87 pos = strrchr(value, '"'); 88 if (pos == NULL || pos[1] != '\0') 89 return NULL; 90 *len = pos - value; 91 str = malloc(*len + 1); 92 if (str == NULL) 93 return NULL; 94 memcpy(str, value, *len); 95 str[*len] = '\0'; 96 return str; 97 } else { 98 u8 *str; 99 size_t tlen, hlen = strlen(value); 100 if (hlen & 1) 101 return NULL; 102 tlen = hlen / 2; 103 str = malloc(tlen + 1); 104 if (str == NULL) 105 return NULL; 106 if (hexstr2bin(value, str, tlen)) { 107 free(str); 108 return NULL; 109 } 110 str[tlen] = '\0'; 111 *len = tlen; 112 return (char *) str; 113 } 114} 115 116 117static 118int gct_config_parse_str(const struct parse_data *data, 119 struct gct_config *config, 120 int line, const char *value) 121{ 122 size_t res_len, *dst_len; 123 char **dst, *tmp; 124 125 if (strcmp(value, "NULL") == 0) { 126 wmlog_msg(4, "Unset configuration string '%s'", 127 data->name); 128 tmp = NULL; 129 res_len = 0; 130 goto set; 131 } 132 133 tmp = gct_config_parse_string(value, &res_len); 134 if (tmp == NULL) { 135 wmlog_msg(0, "Line %d: failed to parse %s '%s'.", 136 line, data->name, 137 data->key_data ? "[KEY DATA REMOVED]" : value); 138 return -1; 139 } 140 wmlog_dumphexasc(4, (u8 *) tmp, res_len, "%s", data->name); 141/* 142 if (data->key_data) { 143 gct_hexdump_ascii_key(MSG_MSGDUMP, data->name, 144 (u8 *) tmp, res_len); 145 } else { 146 gct_hexdump_ascii(MSG_MSGDUMP, data->name, 147 (u8 *) tmp, res_len); 148 } 149*/ 150 if (data->param3 && res_len < (size_t) data->param3) { 151 wmlog_msg(0, "E: Line %d: too short %s (len=%lu " 152 "min_len=%ld)", line, data->name, 153 (unsigned long) res_len, (long) data->param3); 154 free(tmp); 155 return -1; 156 } 157 158 if (data->param4 && res_len > (size_t) data->param4) { 159 wmlog_msg(0, "E: Line %d: too long %s (len=%lu " 160 "max_len=%ld)", line, data->name, 161 (unsigned long) res_len, (long) data->param4); 162 free(tmp); 163 return -1; 164 } 165 166set: 167 dst = (char **) (((u8 *) config) + (long) data->param1); 168 dst_len = (size_t *) (((u8 *) config) + (long) data->param2); 169 free(*dst); 170 *dst = tmp; 171 if (data->param2) 172 *dst_len = res_len; 173 174 return 0; 175} 176 177 178static 179int gct_config_parse_int(const struct parse_data *data, 180 struct gct_config *config, 181 int line, const char *value) 182{ 183 int *dst; 184 185 dst = (int *) (((u8 *) config) + (long) data->param1); 186 *dst = atoi(value); 187 wmlog_msg(4, "%s=%d (0x%x)", data->name, *dst, *dst); 188 189 if (data->param3 && *dst < (long) data->param3) { 190 wmlog_msg(0, "E: Line %d: too small %s (value=%d " 191 "min_value=%ld)", line, data->name, *dst, 192 (long) data->param3); 193 *dst = (long) data->param3; 194 return -1; 195 } 196 197 if (data->param4 && *dst > (long) data->param4) { 198 wmlog_msg(0, "E: Line %d: too large %s (value=%d " 199 "max_value=%ld)", line, data->name, *dst, 200 (long) data->param4); 201 *dst = (long) data->param4; 202 return -1; 203 } 204 205 return 0; 206} 207 208static 209int gct_config_parse_eap(const struct parse_data *data, 210 struct gct_config *config, int line, 211 const char *value) 212{ 213 int last, errors = 0; 214 char *start, *end, *buf; 215 struct eap_method_type *methods = NULL, *tmp; 216 size_t num_methods = 0; 217 218 buf = strdup(value); 219 if (buf == NULL) 220 return -1; 221 start = buf; 222 223 while (*start != '\0') { 224 while (*start == ' ' || *start == '\t') 225 start++; 226 if (*start == '\0') 227 break; 228 end = start; 229 while (*end != ' ' && *end != '\t' && *end != '\0') 230 end++; 231 last = *end == '\0'; 232 *end = '\0'; 233 tmp = methods; 234 methods = realloc(methods, 235 (num_methods + 1) * sizeof(*methods)); 236 if (methods == NULL) { 237 free(tmp); 238 free(buf); 239 return -1; 240 } 241 methods[num_methods].method = eap_peer_get_type( 242 start, &methods[num_methods].vendor); 243 if (methods[num_methods].vendor == EAP_VENDOR_IETF && 244 methods[num_methods].method == EAP_TYPE_NONE) { 245 wmlog_msg(0, "Line %d: unknown EAP method " 246 "'%s'", line, start); 247 wmlog_msg(0, "You may need to add support for" 248 " this EAP method during wpa_supplicant\n" 249 "build time configuration.\n" 250 "See README for more information."); 251 errors++; 252 }/* else if (methods[num_methods].vendor == EAP_VENDOR_IETF && 253 methods[num_methods].method == EAP_TYPE_LEAP) 254 ssid->leap++; 255 else 256 ssid->non_leap++;*/ 257 num_methods++; 258 if (last) 259 break; 260 start = end + 1; 261 } 262 free(buf); 263 264 tmp = methods; 265 methods = realloc(methods, (num_methods + 1) * sizeof(*methods)); 266 if (methods == NULL) { 267 free(tmp); 268 return -1; 269 } 270 methods[num_methods].vendor = EAP_VENDOR_IETF; 271 methods[num_methods].method = EAP_TYPE_NONE; 272 num_methods++; 273 274 wmlog_dumphexasc(4, (u8 *) methods, 275 num_methods * sizeof(*methods),"eap methods"); 276 config->eap_methods = methods; 277 return errors ? -1 : 0; 278} 279 280 281static 282int gct_config_parse_password(const struct parse_data *data, 283 struct gct_config *config, int line, 284 const char *value) 285{ 286 u8 *hash; 287 288 if (strcmp(value, "NULL") == 0) { 289 wmlog_msg(4, "Unset configuration string 'password'\n"); 290 free(config->password); 291 config->password = NULL; 292 config->password_len = 0; 293 return 0; 294 } 295 296 if (strncmp(value, "hash:", 5) != 0) { 297 char *tmp; 298 size_t res_len; 299 300 tmp = gct_config_parse_string(value, &res_len); 301 if (tmp == NULL) { 302 wmlog_msg(0, "E: Line %d: failed to parse " 303 "password.", line); 304 return -1; 305 } 306 wmlog_dumphexasc(4, (u8 *) tmp, res_len,"%s",data->name); 307// gct_hexdump_ascii_key(MSG_MSGDUMP, data->name, 308// (u8 *) tmp, res_len); 309 310 free(config->password); 311 config->password = (u8 *) tmp; 312 config->password_len = res_len; 313 config->flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; 314 315 return 0; 316 } 317 318 319 /* NtPasswordHash: hash:<32 hex digits> */ 320 if (strlen(value + 5) != 2 * 16) { 321 wmlog_msg(0, "E: Line %d: Invalid password hash length " 322 "(expected 32 hex digits)", line); 323 return -1; 324 } 325 326 hash = malloc(16); 327 if (hash == NULL) 328 return -1; 329 330 if (hexstr2bin(value + 5, hash, 16)) { 331 free(hash); 332 wmlog_msg(0, "E: Line %d: Invalid password hash", line); 333 return -1; 334 } 335 336 wmlog_dumphexasc(4, hash, 16,"%s",data->name); 337// gct_hexdump_key(MSG_MSGDUMP, data->name, hash, 16); 338 339 free(config->password); 340 config->password = hash; 341 config->password_len = 16; 342 config->flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH; 343 344 return 0; 345} 346 347 348#define OFFSET(v) ((void *) &((struct gct_config *) 0)->v) 349 350#define _STR(f) #f, gct_config_parse_str, OFFSET(f) 351#define STR(f) _STR(f), NULL, NULL, NULL, 0 352#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1 353#define _STR_LEN(f) _STR(f), OFFSET(f ## _len) 354#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0 355#define _INT(f) #f, gct_config_parse_int, OFFSET(f), (void *) 0 356#define INT(f) _INT(f), NULL, NULL, 0 357#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0 358 359#define _FUNC(f) #f, gct_config_parse_ ## f, NULL, NULL, NULL, NULL 360#define FUNC(f) _FUNC(f), 0 361#define FUNC_KEY(f) _FUNC(f), 1 362 363static 364const struct parse_data ssid_fields[] = { 365 366 { INT_RANGE(nspid, 0, 0xffffff) }, 367 { INT_RANGE(use_pkm, 0, 1) }, 368 { INT_RANGE(use_nv, 0, 1) }, 369 { INT_RANGE(eap_type, -1, 7) }, 370 { INT_RANGE(ca_cert_null, 0, 1) }, 371 { INT_RANGE(dev_cert_null, 0, 1) }, 372 { INT_RANGE(cert_nv, 0, 1) }, 373 { INT_RANGE(wimax_verbose_level, -1, 3) }, 374 { INT_RANGE(wpa_debug_level, 0, 4) }, 375 { STR(log_file) }, 376 { STR(event_script) }, 377 { FUNC(eap) }, 378 { STR_LEN(identity) }, 379 { STR_LEN(anonymous_identity) }, 380 { FUNC_KEY(password) }, 381 { STR(ca_cert) }, 382 { STR(client_cert) }, 383 { STR(private_key) }, 384 { STR_KEY(private_key_passwd) }, 385 { STR(phase1) }, 386 { STR(phase2) }, 387}; 388 389#undef OFFSET 390#undef _STR 391#undef STR 392#undef STR_KEY 393#undef _STR_LEN 394#undef STR_LEN 395#undef _INT 396#undef INT 397#undef INT_RANGE 398#undef _FUNC 399#undef FUNC 400#undef FUNC_KEY 401#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0])) 402 403static 404int gct_config_set(struct gct_config *config, const char *var, const char *value, 405 int line) 406{ 407 size_t i; 408 int ret = 0; 409 410 if (config == NULL || var == NULL || value == NULL) 411 return -1; 412 413 for (i = 0; i < NUM_SSID_FIELDS; i++) { 414 const struct parse_data *field = &ssid_fields[i]; 415 if (strcmp(var, field->name) != 0) 416 continue; 417 418 if (field->parser(field, config, line, value)) { 419 if (line) { 420 wmlog_msg(0, "E: Line %d: failed to " 421 "parse %s '%s'.", line, var, value); 422 } 423 ret = -1; 424 } 425 break; 426 } 427 if (i == NUM_SSID_FIELDS) { 428 if (line) { 429 wmlog_msg(0, "E: Line %d: unknown network field " 430 "'%s'.", line, var); 431 } 432 ret = -1; 433 } 434 435 return ret; 436} 437 438 439static 440char * gct_config_get_line(char *s, int size, FILE *stream, int *line, 441 char **_pos) 442{ 443 char *pos, *end, *sstart; 444 445 while (fgets(s, size, stream)) { 446 (*line)++; 447 s[size - 1] = '\0'; 448 pos = s; 449 450 /* Skip white space from the beginning of line. */ 451 while (*pos == ' ' || *pos == '\t' || *pos == '\r') 452 pos++; 453 454 /* Skip comment lines and empty lines */ 455 if (*pos == '#' || *pos == '\n' || *pos == '\0' || *pos == '[') 456 continue; 457 458 /* 459 * Remove # comments unless they are within a double quoted 460 * string. 461 */ 462 sstart = strchr(pos, '"'); 463 if (sstart) 464 sstart = strrchr(sstart + 1, '"'); 465 if (!sstart) 466 sstart = pos; 467 end = strchr(sstart, '#'); 468 if (end) 469 *end-- = '\0'; 470 else 471 end = pos + strlen(pos) - 1; 472 473 /* Remove trailing white space. */ 474 while (end > pos && 475 (*end == '\n' || *end == ' ' || *end == '\t' || 476 *end == '\r')) 477 *end-- = '\0'; 478 479 if (*pos == '\0') 480 continue; 481 482 if (_pos) 483 *_pos = pos; 484 return pos; 485 } 486 487 if (_pos) 488 *_pos = NULL; 489 return NULL; 490} 491 492 493static 494int gct_config_validate_network(struct gct_config *config) 495{ 496 int errors = 0; 497 498 if (config->use_pkm == 0) 499 errors++; 500 if (config->eap_type == 0) 501 errors++; 502 503 return errors; 504} 505 506 507static 508int gct_config_read_network(struct gct_config *config, FILE *f, int *line) 509{ 510 int errors = 0; 511 char buf[256], *pos, *pos2; 512 513 while (gct_config_get_line(buf, sizeof(buf), f, line, &pos)) { 514 515 pos2 = strchr(pos, '='); 516 if (pos2 == NULL) { 517 wmlog_msg(0, "E: Line %d: Invalid CFG line " 518 "'%s'.", *line, pos); 519 errors++; 520 continue; 521 } 522 523 *pos2++ = '\0'; 524 if (*pos2 == '"') { 525 if (strchr(pos2 + 1, '"') == NULL) { 526 wmlog_msg(0, "E: Line %d: invalid " 527 "quotation '%s'.", *line, pos2); 528 errors++; 529 continue; 530 } 531 } 532 533 if (gct_config_set(config, pos, pos2, *line) < 0) 534 errors++; 535 536 } 537 538 errors += gct_config_validate_network(config); 539 540 return errors; 541} 542 543 544static 545struct gct_config * gct_config_alloc_empty() 546{ 547 struct gct_config *config; 548 549 config = calloc(1, sizeof(*config)); 550 if (config == NULL) 551 return NULL; 552 return config; 553} 554 555 556void gct_config_free(struct gct_config *config) 557{ 558 free(config->eap_methods); 559 free(config->identity); 560 free(config->anonymous_identity); 561 free(config->password); 562 free(config->ca_cert); 563 free(config->client_cert); 564 free(config->private_key); 565 free(config->private_key_passwd); 566 free(config->phase1); 567 free(config->phase2); 568 free(config); 569} 570 571 572struct gct_config * gct_config_read(const char *name) 573{ 574 FILE *f; 575// char buf[256], *pos; 576 int errors = 0, line = 0; 577 struct gct_config *config; 578 579 config = gct_config_alloc_empty(); 580 if (config == NULL) 581 return NULL; 582 if (eap_peer_register_methods() < 0) 583 return NULL; 584 wmlog_msg(3, "Reading configuration file '%s'", name); 585 f = fopen(name, "r"); 586 if (f == NULL) { 587 free(config); 588 wmlog_msg(0, "Configuration file error: %s",strerror(errno)); 589 return NULL; 590 } 591 592// while (gct_config_get_line(buf, sizeof(buf), f, &line, &pos)) { 593 errors += gct_config_read_network(config, f, &line); 594// } 595 596 fclose(f); 597 598 if (errors) { 599 gct_config_free(config); 600 config = NULL; 601 } 602 eap_peer_unregister_methods(); 603 return config; 604} 605 606