Line data Source code
1 : /*
2 : * Copyright (C) 2005-2012 Free Software Foundation, Inc.
3 : *
4 : * Author: Nikos Mavrogiannopoulos
5 : *
6 : * This file is part of GnuTLS.
7 : *
8 : * The GnuTLS is free software; you can redistribute it and/or
9 : * modify it under the terms of the GNU Lesser General Public License
10 : * as published by the Free Software Foundation; either version 2.1 of
11 : * the License, or (at your option) any later version.
12 : *
13 : * This library is distributed in the hope that it will be useful, but
14 : * WITHOUT ANY WARRANTY; without even the implied warranty of
15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : * Lesser General Public License for more details.
17 : *
18 : * You should have received a copy of the GNU Lesser General Public License
19 : * along with this program. If not, see <https://www.gnu.org/licenses/>
20 : *
21 : */
22 :
23 : /* Functions for operating in an PSK passwd file are included here */
24 :
25 : #include "gnutls_int.h"
26 :
27 : #include "x509_b64.h"
28 : #include "errors.h"
29 : #include <auth/psk_passwd.h>
30 : #include <auth/psk.h>
31 : #include "auth.h"
32 : #include "dh.h"
33 : #include "debug.h"
34 : #include <str.h>
35 : #include <datum.h>
36 : #include <num.h>
37 : #include <random.h>
38 :
39 :
40 : /* this function parses passwd.psk file. Format is:
41 : * string(username):hex(passwd)
42 : */
43 3073 : static int pwd_put_values(gnutls_datum_t * psk, char *str)
44 : {
45 3073 : char *p;
46 3073 : int len, ret;
47 3073 : gnutls_datum_t tmp;
48 :
49 3073 : p = strchr(str, ':');
50 3073 : if (p == NULL) {
51 0 : gnutls_assert();
52 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
53 : }
54 :
55 3073 : *p = '\0';
56 3073 : p++;
57 :
58 : /* skip username
59 : */
60 :
61 : /* read the key
62 : */
63 3073 : len = strlen(p);
64 3073 : if (p[len - 1] == '\n' || p[len - 1] == ' ')
65 3073 : len--;
66 :
67 3073 : tmp.data = (void*)p;
68 3073 : tmp.size = len;
69 3073 : ret = gnutls_hex_decode2(&tmp, psk);
70 3073 : if (ret < 0) {
71 130 : gnutls_assert();
72 130 : return ret;
73 : }
74 :
75 : return 0;
76 : }
77 :
78 10163 : static bool username_matches(const gnutls_datum_t *username,
79 : const char *line, size_t line_size)
80 : {
81 10163 : int retval;
82 10163 : unsigned i;
83 10163 : gnutls_datum_t hexline, hex_username = { NULL, 0 };
84 :
85 : /*
86 : * Guard against weird behavior - we don't check 'line',
87 : * as it's returned by getline(), which will never return NULL
88 : * if successful.
89 : */
90 10163 : if (username->data == NULL)
91 : return false;
92 :
93 10163 : if (line_size == 0)
94 0 : return (username->size == 0);
95 :
96 : /* move to first ':' */
97 : i = 0;
98 68510 : while ((i < line_size) && (line[i] != '\0')
99 68510 : && (line[i] != ':')) {
100 58347 : i++;
101 : }
102 :
103 : /* if format is in hex, e.g. #FAFAFA */
104 10163 : if (line[0] == '#' && line_size > 1) {
105 4580 : hexline.data = (void *) &line[1];
106 4580 : hexline.size = i - 1;
107 :
108 4580 : if (gnutls_hex_decode2(&hexline, &hex_username) < 0)
109 0 : return gnutls_assert_val(0);
110 :
111 4580 : if (hex_username.size == username->size)
112 2788 : retval = memcmp(username->data, hex_username.data, username->size);
113 : else
114 : retval = -1;
115 :
116 4580 : _gnutls_free_datum(&hex_username);
117 : } else {
118 5583 : retval = strncmp((const char *) username->data, line, MAX(i, username->size));
119 : }
120 :
121 10163 : return (retval == 0);
122 : }
123 :
124 :
125 : /* Randomizes the given password entry. It actually sets a random password.
126 : * Returns 0 on success.
127 : */
128 136 : static int _randomize_psk(gnutls_datum_t * psk)
129 : {
130 136 : int ret;
131 :
132 136 : psk->data = gnutls_malloc(16);
133 136 : if (psk->data == NULL) {
134 0 : gnutls_assert();
135 0 : return GNUTLS_E_MEMORY_ERROR;
136 : }
137 :
138 136 : psk->size = 16;
139 :
140 136 : ret = gnutls_rnd(GNUTLS_RND_NONCE, (char *) psk->data, 16);
141 136 : if (ret < 0) {
142 0 : gnutls_assert();
143 0 : return ret;
144 : }
145 :
146 : return 0;
147 : }
148 :
149 : /* Returns the PSK key of the given user.
150 : * If the user doesn't exist a random password is returned instead.
151 : */
152 : int
153 3868 : _gnutls_psk_pwd_find_entry(gnutls_session_t session,
154 : const char *username, uint16_t username_len,
155 : gnutls_datum_t * psk)
156 : {
157 3868 : gnutls_psk_server_credentials_t cred;
158 3868 : FILE *fp;
159 3868 : char *line = NULL;
160 3868 : size_t line_size = 0;
161 3868 : int ret;
162 3868 : gnutls_datum_t username_datum = {
163 : .data = (unsigned char *) username,
164 : .size = username_len
165 : };
166 :
167 3868 : cred = (gnutls_psk_server_credentials_t)
168 3868 : _gnutls_get_cred(session, GNUTLS_CRD_PSK);
169 3868 : if (cred == NULL) {
170 0 : gnutls_assert();
171 0 : return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
172 : }
173 :
174 : /* if the callback which sends the parameters is
175 : * set, use it.
176 : */
177 3868 : if (cred->pwd_callback != NULL) {
178 659 : ret = cred->pwd_callback(session, &username_datum, psk);
179 :
180 659 : if (ret == 1) { /* the user does not exist */
181 0 : ret = _randomize_psk(psk);
182 0 : if (ret < 0) {
183 0 : gnutls_assert();
184 0 : return ret;
185 : }
186 : return 0;
187 : }
188 :
189 659 : if (ret < 0) {
190 0 : gnutls_assert();
191 0 : return GNUTLS_E_SRP_PWD_ERROR;
192 : }
193 :
194 : return 0;
195 : }
196 :
197 : /* The callback was not set. Proceed.
198 : */
199 3209 : if (cred->password_file == NULL) {
200 0 : gnutls_assert();
201 0 : return GNUTLS_E_SRP_PWD_ERROR;
202 : }
203 :
204 : /* Open the selected password file.
205 : */
206 3209 : fp = fopen(cred->password_file, "re");
207 3209 : if (fp == NULL) {
208 0 : gnutls_assert();
209 0 : return GNUTLS_E_SRP_PWD_ERROR;
210 : }
211 :
212 10299 : while (getline(&line, &line_size, fp) > 0) {
213 10163 : if (username_matches(&username_datum, line, line_size)) {
214 3073 : ret = pwd_put_values(psk, line);
215 3073 : if (ret < 0) {
216 130 : gnutls_assert();
217 130 : ret = GNUTLS_E_SRP_PWD_ERROR;
218 130 : goto cleanup;
219 : }
220 2943 : ret = 0;
221 2943 : goto cleanup;
222 : }
223 : }
224 :
225 : /* user was not found. Fake him.
226 : */
227 136 : ret = _randomize_psk(psk);
228 136 : if (ret < 0) {
229 0 : goto cleanup;
230 : }
231 :
232 : ret = 0;
233 3209 : cleanup:
234 3209 : if (fp != NULL)
235 3209 : fclose(fp);
236 :
237 3209 : zeroize_key(line, line_size);
238 3209 : free(line);
239 :
240 3209 : return ret;
241 :
242 : }
243 :
244 : /* returns the username and they key for the PSK session.
245 : * Free is non (0) if they have to be freed.
246 : */
247 617 : int _gnutls_find_psk_key(gnutls_session_t session,
248 : gnutls_psk_client_credentials_t cred,
249 : gnutls_datum_t * username, gnutls_datum_t * key,
250 : int *free)
251 : {
252 617 : int ret;
253 :
254 617 : *free = 0;
255 :
256 617 : if (cred->username.data != NULL && cred->key.data != NULL) {
257 616 : username->data = cred->username.data;
258 616 : username->size = cred->username.size;
259 616 : key->data = cred->key.data;
260 616 : key->size = cred->key.size;
261 1 : } else if (cred->get_function != NULL) {
262 1 : ret = cred->get_function(session, username, key);
263 :
264 1 : if (ret)
265 0 : return gnutls_assert_val(ret);
266 :
267 1 : *free = 1;
268 : } else
269 0 : return
270 0 : gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
271 :
272 : return 0;
273 : }
|