1/* 2 * WPA Supplicant / Configuration backend: text file 3 * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 * 14 * This file implements a configuration backend for text files. All the 15 * configuration information is stored in a text file that uses a format 16 * described in the sample configuration file, wpa_supplicant.conf. 17 */ 18 19#include "includes.h" 20 21#include "common.h" 22#include "config.h" 23#include "base64.h" 24#include "eap_methods.h" 25 26#include <sys/stat.h> 27 28 29/** 30 * wpa_config_get_line - Read the next configuration file line 31 * @s: Buffer for the line 32 * @size: The buffer length 33 * @stream: File stream to read from 34 * @line: Pointer to a variable storing the file line number 35 * @_pos: Buffer for the pointer to the beginning of data on the text line or 36 * %NULL if not needed (returned value used instead) 37 * Returns: Pointer to the beginning of data on the text line or %NULL if no 38 * more text lines are available. 39 * 40 * This function reads the next non-empty line from the configuration file and 41 * removes comments. The returned string is guaranteed to be null-terminated. 42 */ 43static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line, 44 char **_pos) 45{ 46 char *pos, *end, *sstart; 47 48 while (fgets(s, size, stream)) { 49 (*line)++; 50 s[size - 1] = '\0'; 51 pos = s; 52 53 /* Skip white space from the beginning of line. */ 54 while (*pos == ' ' || *pos == '\t' || *pos == '\r') 55 pos++; 56 57 /* Skip comment lines and empty lines */ 58 if (*pos == '#' || *pos == '\n' || *pos == '\0') 59 continue; 60 61 /* 62 * Remove # comments unless they are within a double quoted 63 * string. 64 */ 65 sstart = os_strchr(pos, '"'); 66 if (sstart) 67 sstart = os_strrchr(sstart + 1, '"'); 68 if (!sstart) 69 sstart = pos; 70 end = os_strchr(sstart, '#'); 71 if (end) 72 *end-- = '\0'; 73 else 74 end = pos + os_strlen(pos) - 1; 75 76 /* Remove trailing white space. */ 77 while (end > pos && 78 (*end == '\n' || *end == ' ' || *end == '\t' || 79 *end == '\r')) 80 *end-- = '\0'; 81 82 if (*pos == '\0') 83 continue; 84 85 if (_pos) 86 *_pos = pos; 87 return pos; 88 } 89 90 if (_pos) 91 *_pos = NULL; 92 return NULL; 93} 94 95 96static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) 97{ 98 int errors = 0; 99 100 if (ssid->passphrase) { 101 if (ssid->psk_set) { 102 wpa_printf(MSG_ERROR, "Line %d: both PSK and " 103 "passphrase configured.", line); 104 errors++; 105 } 106 wpa_config_update_psk(ssid); 107 } 108 109 if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !ssid->psk_set) { 110 wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " 111 "management, but no PSK configured.", line); 112 errors++; 113 } 114 115 if ((ssid->group_cipher & WPA_CIPHER_CCMP) && 116 !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && 117 !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { 118 /* Group cipher cannot be stronger than the pairwise cipher. */ 119 wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher" 120 " list since it was not allowed for pairwise " 121 "cipher", line); 122 ssid->group_cipher &= ~WPA_CIPHER_CCMP; 123 } 124 125 return errors; 126} 127 128 129static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) 130{ 131 struct wpa_ssid *ssid; 132 int errors = 0, end = 0; 133 char buf[256], *pos, *pos2; 134 135 wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", 136 *line); 137 ssid = os_zalloc(sizeof(*ssid)); 138 if (ssid == NULL) 139 return NULL; 140 ssid->id = id; 141 142 wpa_config_set_network_defaults(ssid); 143 144 while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { 145 if (os_strcmp(pos, "}") == 0) { 146 end = 1; 147 break; 148 } 149 150 pos2 = os_strchr(pos, '='); 151 if (pos2 == NULL) { 152 wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line " 153 "'%s'.", *line, pos); 154 errors++; 155 continue; 156 } 157 158 *pos2++ = '\0'; 159 if (*pos2 == '"') { 160 if (os_strchr(pos2 + 1, '"') == NULL) { 161 wpa_printf(MSG_ERROR, "Line %d: invalid " 162 "quotation '%s'.", *line, pos2); 163 errors++; 164 continue; 165 } 166 } 167 168 if (wpa_config_set(ssid, pos, pos2, *line) < 0) 169 errors++; 170 } 171 172 if (!end) { 173 wpa_printf(MSG_ERROR, "Line %d: network block was not " 174 "terminated properly.", *line); 175 errors++; 176 } 177 178 errors += wpa_config_validate_network(ssid, *line); 179 180 if (errors) { 181 wpa_config_free_ssid(ssid); 182 ssid = NULL; 183 } 184 185 return ssid; 186} 187 188 189static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line, 190 const char *name) 191{ 192 struct wpa_config_blob *blob; 193 char buf[256], *pos; 194 unsigned char *encoded = NULL, *nencoded; 195 int end = 0; 196 size_t encoded_len = 0, len; 197 198 wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new named blob '%s'", 199 *line, name); 200 201 while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { 202 if (os_strcmp(pos, "}") == 0) { 203 end = 1; 204 break; 205 } 206 207 len = os_strlen(pos); 208 nencoded = os_realloc(encoded, encoded_len + len); 209 if (nencoded == NULL) { 210 wpa_printf(MSG_ERROR, "Line %d: not enough memory for " 211 "blob", *line); 212 os_free(encoded); 213 return NULL; 214 } 215 encoded = nencoded; 216 os_memcpy(encoded + encoded_len, pos, len); 217 encoded_len += len; 218 } 219 220 if (!end) { 221 wpa_printf(MSG_ERROR, "Line %d: blob was not terminated " 222 "properly", *line); 223 os_free(encoded); 224 return NULL; 225 } 226 227 blob = os_zalloc(sizeof(*blob)); 228 if (blob == NULL) { 229 os_free(encoded); 230 return NULL; 231 } 232 blob->name = os_strdup(name); 233 blob->data = base64_decode(encoded, encoded_len, &blob->len); 234 os_free(encoded); 235 236 if (blob->name == NULL || blob->data == NULL) { 237 wpa_config_free_blob(blob); 238 return NULL; 239 } 240 241 return blob; 242} 243 244 245struct wpa_config * wpa_config_read(const char *name) 246{ 247 FILE *f; 248 char buf[256], *pos; 249 int errors = 0, line = 0; 250 struct wpa_ssid *ssid, *tail = NULL, *head = NULL; 251 struct wpa_config *config; 252 int id = 0; 253 254 config = wpa_config_alloc_empty(NULL, NULL); 255 if (config == NULL) 256 return NULL; 257 wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); 258 f = fopen(name, "r"); 259 if (f == NULL) { 260 os_free(config); 261 return NULL; 262 } 263 /* When creating the config file, give group read/write access 264 * to allow backup and restoring the file. 265 */ 266 chmod(name, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); 267 268 while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) { 269 if (os_strcmp(pos, "network={") == 0) { 270 ssid = wpa_config_read_network(f, &line, id++); 271 if (ssid == NULL) { 272 wpa_printf(MSG_ERROR, "Line %d: failed to " 273 "parse network block.", line); 274#ifndef WPA_IGNORE_CONFIG_ERRORS 275 errors++; 276#endif 277 continue; 278 } 279 if (head == NULL) { 280 head = tail = ssid; 281 } else { 282 tail->next = ssid; 283 tail = ssid; 284 } 285 if (wpa_config_add_prio_network(config, ssid)) { 286 wpa_printf(MSG_ERROR, "Line %d: failed to add " 287 "network block to priority list.", 288 line); 289 errors++; 290 continue; 291 } 292 } else if (os_strncmp(pos, "blob-base64-", 12) == 0) { 293 char *bname = pos + 12, *name_end; 294 struct wpa_config_blob *blob; 295 296 name_end = os_strchr(bname, '='); 297 if (name_end == NULL) { 298 wpa_printf(MSG_ERROR, "Line %d: no blob name " 299 "terminator", line); 300 errors++; 301 continue; 302 } 303 *name_end = '\0'; 304 305 blob = wpa_config_read_blob(f, &line, bname); 306 if (blob == NULL) { 307 wpa_printf(MSG_ERROR, "Line %d: failed to read" 308 " blob %s", line, bname); 309 errors++; 310 continue; 311 } 312 wpa_config_set_blob(config, blob); 313#ifdef CONFIG_CTRL_IFACE 314 } else if (os_strncmp(pos, "ctrl_interface=", 15) == 0) { 315 os_free(config->ctrl_interface); 316 config->ctrl_interface = os_strdup(pos + 15); 317 wpa_printf(MSG_DEBUG, "ctrl_interface='%s'", 318 config->ctrl_interface); 319 } else if (os_strncmp(pos, "ctrl_interface_group=", 21) == 0) { 320 os_free(config->ctrl_interface_group); 321 config->ctrl_interface_group = os_strdup(pos + 21); 322 wpa_printf(MSG_DEBUG, "ctrl_interface_group='%s' " 323 "(DEPRECATED)", 324 config->ctrl_interface_group); 325#endif /* CONFIG_CTRL_IFACE */ 326 } else if (os_strncmp(pos, "eapol_version=", 14) == 0) { 327 config->eapol_version = atoi(pos + 14); 328 if (config->eapol_version < 1 || 329 config->eapol_version > 2) { 330 wpa_printf(MSG_ERROR, "Line %d: Invalid EAPOL " 331 "version (%d): '%s'.", 332 line, config->eapol_version, pos); 333 errors++; 334 continue; 335 } 336 wpa_printf(MSG_DEBUG, "eapol_version=%d", 337 config->eapol_version); 338 } else if (os_strncmp(pos, "ap_scan=", 8) == 0) { 339 config->ap_scan = atoi(pos + 8); 340 wpa_printf(MSG_DEBUG, "ap_scan=%d", config->ap_scan); 341 } else if (os_strncmp(pos, "fast_reauth=", 12) == 0) { 342 config->fast_reauth = atoi(pos + 12); 343 wpa_printf(MSG_DEBUG, "fast_reauth=%d", 344 config->fast_reauth); 345 } else if (os_strncmp(pos, "opensc_engine_path=", 19) == 0) { 346 os_free(config->opensc_engine_path); 347 config->opensc_engine_path = os_strdup(pos + 19); 348 wpa_printf(MSG_DEBUG, "opensc_engine_path='%s'", 349 config->opensc_engine_path); 350 } else if (os_strncmp(pos, "pkcs11_engine_path=", 19) == 0) { 351 os_free(config->pkcs11_engine_path); 352 config->pkcs11_engine_path = os_strdup(pos + 19); 353 wpa_printf(MSG_DEBUG, "pkcs11_engine_path='%s'", 354 config->pkcs11_engine_path); 355 } else if (os_strncmp(pos, "pkcs11_module_path=", 19) == 0) { 356 os_free(config->pkcs11_module_path); 357 config->pkcs11_module_path = os_strdup(pos + 19); 358 wpa_printf(MSG_DEBUG, "pkcs11_module_path='%s'", 359 config->pkcs11_module_path); 360 } else if (os_strncmp(pos, "driver_param=", 13) == 0) { 361 os_free(config->driver_param); 362 config->driver_param = os_strdup(pos + 13); 363 wpa_printf(MSG_DEBUG, "driver_param='%s'", 364 config->driver_param); 365 } else if (os_strncmp(pos, "dot11RSNAConfigPMKLifetime=", 27) 366 == 0) { 367 config->dot11RSNAConfigPMKLifetime = atoi(pos + 27); 368 wpa_printf(MSG_DEBUG, "dot11RSNAConfigPMKLifetime=%d", 369 config->dot11RSNAConfigPMKLifetime); 370 } else if (os_strncmp(pos, 371 "dot11RSNAConfigPMKReauthThreshold=", 34) 372 == 0) { 373 config->dot11RSNAConfigPMKReauthThreshold = 374 atoi(pos + 34); 375 wpa_printf(MSG_DEBUG, 376 "dot11RSNAConfigPMKReauthThreshold=%d", 377 config->dot11RSNAConfigPMKReauthThreshold); 378 } else if (os_strncmp(pos, "dot11RSNAConfigSATimeout=", 25) == 379 0) { 380 config->dot11RSNAConfigSATimeout = atoi(pos + 25); 381 wpa_printf(MSG_DEBUG, "dot11RSNAConfigSATimeout=%d", 382 config->dot11RSNAConfigSATimeout); 383 } else if (os_strncmp(pos, "update_config=", 14) == 0) { 384 config->update_config = atoi(pos + 14); 385 wpa_printf(MSG_DEBUG, "update_config=%d", 386 config->update_config); 387 } else if (os_strncmp(pos, "load_dynamic_eap=", 17) == 0) { 388 char *so = pos + 17; 389 int ret; 390 wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so); 391 ret = eap_peer_method_load(so); 392 if (ret == -2) { 393 wpa_printf(MSG_DEBUG, "This EAP type was " 394 "already loaded - not reloading."); 395 } else if (ret) { 396 wpa_printf(MSG_ERROR, "Line %d: Failed to " 397 "load dynamic EAP method '%s'.", 398 line, so); 399 errors++; 400 } 401 } else { 402 wpa_printf(MSG_ERROR, "Line %d: Invalid configuration " 403 "line '%s'.", line, pos); 404 errors++; 405 continue; 406 } 407 } 408 409 fclose(f); 410 411 config->ssid = head; 412 wpa_config_debug_dump_networks(config); 413 414#ifndef WPA_IGNORE_CONFIG_ERRORS 415 if (errors) { 416 wpa_config_free(config); 417 config = NULL; 418 head = NULL; 419 } 420#endif 421 return config; 422} 423 424 425static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid) 426{ 427 char *value = wpa_config_get(ssid, field); 428 if (value == NULL) 429 return; 430 fprintf(f, "\t%s=%s\n", field, value); 431 os_free(value); 432} 433 434 435static void write_int(FILE *f, const char *field, int value, int def) 436{ 437 if (value == def) 438 return; 439 fprintf(f, "\t%s=%d\n", field, value); 440} 441 442 443static void write_bssid(FILE *f, struct wpa_ssid *ssid) 444{ 445 char *value = wpa_config_get(ssid, "bssid"); 446 if (value == NULL) 447 return; 448 fprintf(f, "\tbssid=%s\n", value); 449 os_free(value); 450} 451 452 453static void write_psk(FILE *f, struct wpa_ssid *ssid) 454{ 455 char *value = wpa_config_get(ssid, "psk"); 456 if (value == NULL) 457 return; 458 fprintf(f, "\tpsk=%s\n", value); 459 os_free(value); 460} 461 462 463static void write_proto(FILE *f, struct wpa_ssid *ssid) 464{ 465 char *value; 466 467 if (ssid->proto == DEFAULT_PROTO) 468 return; 469 470 value = wpa_config_get(ssid, "proto"); 471 if (value == NULL) 472 return; 473 if (value[0]) 474 fprintf(f, "\tproto=%s\n", value); 475 os_free(value); 476} 477 478 479static void write_key_mgmt(FILE *f, struct wpa_ssid *ssid) 480{ 481 char *value; 482 483 if (ssid->key_mgmt == DEFAULT_KEY_MGMT) 484 return; 485 486 value = wpa_config_get(ssid, "key_mgmt"); 487 if (value == NULL) 488 return; 489 if (value[0]) 490 fprintf(f, "\tkey_mgmt=%s\n", value); 491 os_free(value); 492} 493 494 495static void write_pairwise(FILE *f, struct wpa_ssid *ssid) 496{ 497 char *value; 498 499 if (ssid->pairwise_cipher == DEFAULT_PAIRWISE) 500 return; 501 502 value = wpa_config_get(ssid, "pairwise"); 503 if (value == NULL) 504 return; 505 if (value[0]) 506 fprintf(f, "\tpairwise=%s\n", value); 507 os_free(value); 508} 509 510 511static void write_group(FILE *f, struct wpa_ssid *ssid) 512{ 513 char *value; 514 515 if (ssid->group_cipher == DEFAULT_GROUP) 516 return; 517 518 value = wpa_config_get(ssid, "group"); 519 if (value == NULL) 520 return; 521 if (value[0]) 522 fprintf(f, "\tgroup=%s\n", value); 523 os_free(value); 524} 525 526 527static void write_auth_alg(FILE *f, struct wpa_ssid *ssid) 528{ 529 char *value; 530 531 if (ssid->auth_alg == 0) 532 return; 533 534 value = wpa_config_get(ssid, "auth_alg"); 535 if (value == NULL) 536 return; 537 if (value[0]) 538 fprintf(f, "\tauth_alg=%s\n", value); 539 os_free(value); 540} 541 542 543#ifdef IEEE8021X_EAPOL 544static void write_eap(FILE *f, struct wpa_ssid *ssid) 545{ 546 char *value; 547 548 value = wpa_config_get(ssid, "eap"); 549 if (value == NULL) 550 return; 551 552 if (value[0]) 553 fprintf(f, "\teap=%s\n", value); 554 os_free(value); 555} 556#endif /* IEEE8021X_EAPOL */ 557 558 559static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) 560{ 561 char field[20], *value; 562 563 os_snprintf(field, sizeof(field), "wep_key%d", idx); 564 value = wpa_config_get(ssid, field); 565 if (value) { 566 fprintf(f, "\t%s=%s\n", field, value); 567 os_free(value); 568 } 569} 570 571 572static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) 573{ 574 int i; 575 576#define STR(t) write_str(f, #t, ssid) 577#define INT(t) write_int(f, #t, ssid->t, 0) 578#define INT_DEF(t, def) write_int(f, #t, ssid->t, def) 579 580 STR(ssid); 581 INT(scan_ssid); 582 write_bssid(f, ssid); 583 write_psk(f, ssid); 584 write_proto(f, ssid); 585 write_key_mgmt(f, ssid); 586 write_pairwise(f, ssid); 587 write_group(f, ssid); 588 write_auth_alg(f, ssid); 589#ifdef IEEE8021X_EAPOL 590 write_eap(f, ssid); 591 STR(identity); 592 STR(anonymous_identity); 593 STR(eappsk); 594 STR(nai); 595 STR(password); 596 STR(ca_cert); 597 STR(ca_path); 598 STR(client_cert); 599 STR(private_key); 600 STR(private_key_passwd); 601 STR(dh_file); 602 STR(subject_match); 603 STR(altsubject_match); 604 STR(ca_cert2); 605 STR(ca_path2); 606 STR(client_cert2); 607 STR(private_key2); 608 STR(private_key2_passwd); 609 STR(dh_file2); 610 STR(subject_match2); 611 STR(altsubject_match2); 612 STR(phase1); 613 STR(phase2); 614 STR(pcsc); 615 STR(pin); 616 STR(engine_id); 617 STR(key_id); 618 INT(engine); 619 INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); 620#endif /* IEEE8021X_EAPOL */ 621 for (i = 0; i < 4; i++) 622 write_wep_key(f, i, ssid); 623 INT(wep_tx_keyidx); 624 INT(priority); 625#ifdef IEEE8021X_EAPOL 626 INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND); 627 STR(pac_file); 628 INT_DEF(fragment_size, DEFAULT_FRAGMENT_SIZE); 629#endif /* IEEE8021X_EAPOL */ 630 INT(mode); 631 INT(proactive_key_caching); 632 INT(disabled); 633 INT(peerkey); 634#ifdef CONFIG_IEEE80211W 635 INT(ieee80211w); 636#endif /* CONFIG_IEEE80211W */ 637 STR(id_str); 638 639#undef STR 640#undef INT 641#undef INT_DEF 642} 643 644 645static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob) 646{ 647 unsigned char *encoded; 648 649 encoded = base64_encode(blob->data, blob->len, NULL); 650 if (encoded == NULL) 651 return -1; 652 653 fprintf(f, "\nblob-base64-%s={\n%s}\n", blob->name, encoded); 654 os_free(encoded); 655 return 0; 656} 657 658 659static void wpa_config_write_global(FILE *f, struct wpa_config *config) 660{ 661#ifdef CONFIG_CTRL_IFACE 662 if (config->ctrl_interface) 663 fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface); 664 if (config->ctrl_interface_group) 665 fprintf(f, "ctrl_interface_group=%s\n", 666 config->ctrl_interface_group); 667#endif /* CONFIG_CTRL_IFACE */ 668 if (config->eapol_version != DEFAULT_EAPOL_VERSION) 669 fprintf(f, "eapol_version=%d\n", config->eapol_version); 670 if (config->ap_scan != DEFAULT_AP_SCAN) 671 fprintf(f, "ap_scan=%d\n", config->ap_scan); 672 if (config->fast_reauth != DEFAULT_FAST_REAUTH) 673 fprintf(f, "fast_reauth=%d\n", config->fast_reauth); 674 if (config->opensc_engine_path) 675 fprintf(f, "opensc_engine_path=%s\n", 676 config->opensc_engine_path); 677 if (config->pkcs11_engine_path) 678 fprintf(f, "pkcs11_engine_path=%s\n", 679 config->pkcs11_engine_path); 680 if (config->pkcs11_module_path) 681 fprintf(f, "pkcs11_module_path=%s\n", 682 config->pkcs11_module_path); 683 if (config->driver_param) 684 fprintf(f, "driver_param=%s\n", config->driver_param); 685 if (config->dot11RSNAConfigPMKLifetime) 686 fprintf(f, "dot11RSNAConfigPMKLifetime=%d\n", 687 config->dot11RSNAConfigPMKLifetime); 688 if (config->dot11RSNAConfigPMKReauthThreshold) 689 fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%d\n", 690 config->dot11RSNAConfigPMKReauthThreshold); 691 if (config->dot11RSNAConfigSATimeout) 692 fprintf(f, "dot11RSNAConfigSATimeout=%d\n", 693 config->dot11RSNAConfigSATimeout); 694 if (config->update_config) 695 fprintf(f, "update_config=%d\n", config->update_config); 696} 697 698 699int wpa_config_write(const char *name, struct wpa_config *config) 700{ 701 FILE *f; 702 struct wpa_ssid *ssid; 703 struct wpa_config_blob *blob; 704 int ret = 0; 705 706 wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); 707 708 f = fopen(name, "w"); 709 if (f == NULL) { 710 wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name); 711 return -1; 712 } 713 714 wpa_config_write_global(f, config); 715 716 for (ssid = config->ssid; ssid; ssid = ssid->next) { 717 fprintf(f, "\nnetwork={\n"); 718 wpa_config_write_network(f, ssid); 719 fprintf(f, "}\n"); 720 } 721 722 for (blob = config->blobs; blob; blob = blob->next) { 723 ret = wpa_config_write_blob(f, blob); 724 if (ret) 725 break; 726 } 727 728 fclose(f); 729 730 wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully", 731 name, ret ? "un" : ""); 732 return ret; 733} 734