1  /*
2   * External backend for file-backed passwords
3   * Copyright (c) 2021, Patrick Steinhardt <ps@pks.im>
4   *
5   * This software may be distributed under the terms of the BSD license.
6   * See README for more details.
7   */
8  
9  #include "includes.h"
10  
11  #include "utils/common.h"
12  #include "ext_password_i.h"
13  
14  
15  /**
16   * Data structure for the file-backed password backend.
17   */
18  struct ext_password_file_data {
19  	char *path; /* path of the password file */
20  };
21  
22  
23  /**
24   * ext_password_file_init - Initialize file-backed password backend
25   * @params: Parameters passed by the user.
26   * Returns: Pointer to the initialized backend.
27   *
28   * This function initializes a new file-backed password backend. The user is
29   * expected to initialize this backend with the parameters being the path of
30   * the file that contains the passwords.
31   */
ext_password_file_init(const char * params)32  static void * ext_password_file_init(const char *params)
33  {
34  	struct ext_password_file_data *data;
35  
36  	if (!params) {
37  		wpa_printf(MSG_ERROR, "EXT PW FILE: no path given");
38  		return NULL;
39  	}
40  
41  	data = os_zalloc(sizeof(*data));
42  	if (!data)
43  		return NULL;
44  
45  	data->path = os_strdup(params);
46  	if (!data->path) {
47  		os_free(data);
48  		return NULL;
49  	}
50  
51  	return data;
52  }
53  
54  
55  /**
56   * ext_password_file_deinit - Deinitialize file-backed password backend
57   * @ctx: The file-backed password backend
58   *
59   * This function frees all data associated with the file-backed password
60   * backend.
61   */
ext_password_file_deinit(void * ctx)62  static void ext_password_file_deinit(void *ctx)
63  {
64  	struct ext_password_file_data *data = ctx;
65  
66  	str_clear_free(data->path);
67  	os_free(data);
68  }
69  
70  /**
71   * ext_password_file_get - Retrieve password from the file-backed password backend
72   * @ctx: The file-backed password backend
73   * @name: Name of the password to retrieve
74   * Returns: Buffer containing the password if one was found or %NULL.
75   *
76   * This function tries to find a password identified by name in the password
77   * file. The password is expected to be stored in `NAME=PASSWORD` format.
78   * Comments and empty lines in the file are ignored. Invalid lines will cause
79   * an error message, but will not cause the function to fail.
80   */
ext_password_file_get(void * ctx,const char * name)81  static struct wpabuf * ext_password_file_get(void *ctx, const char *name)
82  {
83  	struct ext_password_file_data *data = ctx;
84  	struct wpabuf *password = NULL;
85  	char buf[512], *pos;
86  	size_t name_len;
87  	int line = 0;
88  	FILE *f;
89  
90  	f = fopen(data->path, "r");
91  	if (!f) {
92  		wpa_printf(MSG_ERROR,
93  			   "EXT PW FILE: could not open file '%s': %s",
94  			   data->path, strerror(errno));
95  		return NULL;
96  	}
97  
98  	name_len = os_strlen(name);
99  
100  	wpa_printf(MSG_DEBUG, "EXT PW FILE: get(%s)", name);
101  
102  	while ((pos = fgets(buf, sizeof(buf), f))) {
103  		char *sep;
104  
105  		line++;
106  
107  		/* Strip newline characters */
108  		pos[strcspn(pos, "\r\n")] = 0;
109  
110  		/* Skip comments and empty lines */
111  		if (*pos == '#' || *pos == '\0')
112  			continue;
113  
114  		sep = os_strchr(pos, '=');
115  		if (!sep) {
116  			wpa_printf(MSG_ERROR, "Invalid password line %d.",
117  				   line);
118  			continue;
119  		}
120  
121  		if (!sep[1]) {
122  			wpa_printf(MSG_ERROR, "No password for line %d.", line);
123  			continue;
124  
125  		}
126  
127  		if (name_len != (size_t) (sep - pos) ||
128  		    os_strncmp(name, pos, sep - pos) != 0)
129  			continue;
130  
131  		password = wpabuf_alloc_copy(sep + 1, os_strlen(sep + 1));
132  		goto done;
133  	}
134  
135  	wpa_printf(MSG_ERROR, "Password for '%s' was not found.", name);
136  
137  done:
138  	forced_memzero(buf, sizeof(buf));
139  	fclose(f);
140  	return password;
141  }
142  
143  
144  const struct ext_password_backend ext_password_file = {
145  	.name = "file",
146  	.init = ext_password_file_init,
147  	.deinit = ext_password_file_deinit,
148  	.get = ext_password_file_get,
149  };
150