Line data Source code
1 : /*
2 : * Copyright (C) 2001-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 SRP passwd file are included here */
24 :
25 : #include "gnutls_int.h"
26 :
27 : #ifdef ENABLE_SRP
28 :
29 : #include "x509_b64.h"
30 : #include "errors.h"
31 : #include <auth/srp_passwd.h>
32 : #include <auth/srp_kx.h>
33 : #include "auth.h"
34 : #include "srp.h"
35 : #include "dh.h"
36 : #include "debug.h"
37 : #include <str.h>
38 : #include <datum.h>
39 : #include <num.h>
40 : #include <random.h>
41 : #include <algorithms.h>
42 :
43 : static int _randomize_pwd_entry(SRP_PWD_ENTRY * entry,
44 : gnutls_srp_server_credentials_t cred,
45 : const char * username);
46 :
47 : /* this function parses tpasswd.conf file. Format is:
48 : * string(username):base64(v):base64(salt):int(index)
49 : */
50 9 : static int parse_tpasswd_values(SRP_PWD_ENTRY * entry, char *str)
51 : {
52 9 : char *p;
53 9 : int len, ret;
54 9 : uint8_t *verifier;
55 9 : size_t verifier_size;
56 9 : int indx;
57 :
58 9 : p = strrchr(str, ':'); /* we have index */
59 9 : if (p == NULL) {
60 0 : gnutls_assert();
61 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
62 : }
63 :
64 9 : *p = '\0';
65 9 : p++;
66 :
67 9 : indx = atoi(p);
68 9 : if (indx == 0) {
69 0 : gnutls_assert();
70 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
71 : }
72 :
73 : /* now go for salt */
74 9 : p = strrchr(str, ':'); /* we have salt */
75 9 : if (p == NULL) {
76 0 : gnutls_assert();
77 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
78 : }
79 :
80 9 : *p = '\0';
81 9 : p++;
82 :
83 9 : len = strlen(p);
84 :
85 18 : entry->salt.size =
86 9 : _gnutls_sbase64_decode(p, len, &entry->salt.data);
87 :
88 9 : if (entry->salt.size <= 0) {
89 0 : gnutls_assert();
90 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
91 : }
92 :
93 : /* now go for verifier */
94 9 : p = strrchr(str, ':'); /* we have verifier */
95 9 : if (p == NULL) {
96 0 : _gnutls_free_datum(&entry->salt);
97 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
98 : }
99 :
100 9 : *p = '\0';
101 9 : p++;
102 :
103 9 : len = strlen(p);
104 9 : ret = _gnutls_sbase64_decode(p, len, &verifier);
105 9 : if (ret <= 0) {
106 0 : gnutls_assert();
107 0 : _gnutls_free_datum(&entry->salt);
108 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
109 : }
110 :
111 9 : verifier_size = ret;
112 9 : entry->v.data = verifier;
113 9 : entry->v.size = verifier_size;
114 :
115 : /* now go for username */
116 9 : *p = '\0';
117 :
118 9 : entry->username = gnutls_strdup(str);
119 9 : if (entry->username == NULL) {
120 0 : _gnutls_free_datum(&entry->salt);
121 0 : _gnutls_free_key_datum(&entry->v);
122 0 : gnutls_assert();
123 0 : return GNUTLS_E_MEMORY_ERROR;
124 : }
125 :
126 : return indx;
127 : }
128 :
129 :
130 : /* this function parses tpasswd.conf file. Format is:
131 : * int(index):base64(n):int(g)
132 : */
133 9 : static int parse_tpasswd_conf_values(SRP_PWD_ENTRY * entry, char *str)
134 : {
135 9 : char *p;
136 9 : int len;
137 9 : uint8_t *tmp;
138 9 : int ret;
139 :
140 9 : p = strrchr(str, ':'); /* we have g */
141 9 : if (p == NULL) {
142 0 : gnutls_assert();
143 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
144 : }
145 :
146 9 : *p = '\0';
147 9 : p++;
148 :
149 : /* read the generator */
150 9 : len = strlen(p);
151 9 : if (p[len - 1] == '\n' || p[len - 1] == ' ')
152 9 : len--;
153 9 : ret = _gnutls_sbase64_decode(p, len, &tmp);
154 :
155 9 : if (ret < 0) {
156 0 : gnutls_assert();
157 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
158 : }
159 :
160 9 : entry->g.data = tmp;
161 9 : entry->g.size = ret;
162 :
163 : /* now go for n - modulo */
164 9 : p = strrchr(str, ':'); /* we have n */
165 9 : if (p == NULL) {
166 0 : _gnutls_free_datum(&entry->g);
167 0 : gnutls_assert();
168 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
169 : }
170 :
171 9 : *p = '\0';
172 9 : p++;
173 :
174 9 : len = strlen(p);
175 9 : ret = _gnutls_sbase64_decode(p, len, &tmp);
176 :
177 9 : if (ret < 0) {
178 0 : gnutls_assert();
179 0 : _gnutls_free_datum(&entry->g);
180 0 : return GNUTLS_E_SRP_PWD_PARSING_ERROR;
181 : }
182 :
183 9 : entry->n.data = tmp;
184 9 : entry->n.size = ret;
185 :
186 9 : return 0;
187 : }
188 :
189 :
190 : /* this function opens the tpasswd.conf file and reads the g and n
191 : * values. They are put in the entry.
192 : */
193 : static int
194 9 : pwd_read_conf(const char *pconf_file, SRP_PWD_ENTRY * entry, int idx)
195 : {
196 9 : FILE *fp;
197 9 : char *line = NULL;
198 9 : size_t line_size = 0;
199 9 : unsigned i, len;
200 9 : char indexstr[10];
201 9 : int ret;
202 :
203 9 : snprintf(indexstr, sizeof(indexstr), "%u", (unsigned int) idx);
204 :
205 9 : fp = fopen(pconf_file, "re");
206 9 : if (fp == NULL) {
207 0 : gnutls_assert();
208 0 : return GNUTLS_E_FILE_ERROR;
209 : }
210 :
211 9 : len = strlen(indexstr);
212 24 : while (getline(&line, &line_size, fp) > 0) {
213 : /* move to first ':' */
214 : i = 0;
215 48 : while ((i < line_size) && (line[i] != ':')
216 24 : && (line[i] != '\0')) {
217 24 : i++;
218 : }
219 :
220 24 : if (strncmp(indexstr, line, MAX(i, len)) == 0) {
221 9 : if (parse_tpasswd_conf_values(entry, line) >= 0) {
222 9 : ret = 0;
223 9 : goto cleanup;
224 : } else {
225 0 : ret = GNUTLS_E_SRP_PWD_ERROR;
226 0 : goto cleanup;
227 : }
228 : }
229 : }
230 : ret = GNUTLS_E_SRP_PWD_ERROR;
231 :
232 9 : cleanup:
233 9 : zeroize_key(line, line_size);
234 9 : free(line);
235 9 : fclose(fp);
236 9 : return ret;
237 :
238 : }
239 :
240 : int
241 174 : _gnutls_srp_pwd_read_entry(gnutls_session_t state, char *username,
242 : SRP_PWD_ENTRY ** _entry)
243 : {
244 174 : gnutls_srp_server_credentials_t cred;
245 174 : FILE *fp = NULL;
246 174 : char *line = NULL;
247 174 : size_t line_size = 0;
248 174 : unsigned i, len;
249 174 : int ret;
250 174 : int idx;
251 174 : SRP_PWD_ENTRY *entry = NULL;
252 :
253 174 : *_entry = gnutls_calloc(1, sizeof(SRP_PWD_ENTRY));
254 174 : if (*_entry == NULL) {
255 0 : gnutls_assert();
256 0 : return GNUTLS_E_MEMORY_ERROR;
257 : }
258 174 : entry = *_entry;
259 :
260 174 : cred = (gnutls_srp_server_credentials_t)
261 174 : _gnutls_get_cred(state, GNUTLS_CRD_SRP);
262 174 : if (cred == NULL) {
263 0 : gnutls_assert();
264 0 : ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS;
265 0 : goto cleanup;
266 : }
267 :
268 : /* if the callback which sends the parameters is
269 : * set, use it.
270 : */
271 174 : if (cred->pwd_callback != NULL) {
272 165 : ret = cred->pwd_callback(state, username, &entry->salt,
273 : &entry->v, &entry->g, &entry->n);
274 :
275 165 : if (ret == 1) { /* the user does not exist */
276 0 : if (entry->g.size != 0 && entry->n.size != 0) {
277 0 : ret = _randomize_pwd_entry(entry, cred, username);
278 0 : if (ret < 0) {
279 0 : gnutls_assert();
280 0 : goto cleanup;
281 : }
282 : return 0;
283 : } else {
284 0 : gnutls_assert();
285 : ret = -1; /* error in the callback */
286 : }
287 : }
288 :
289 165 : if (ret < 0) {
290 0 : gnutls_assert();
291 0 : ret = GNUTLS_E_SRP_PWD_ERROR;
292 0 : goto cleanup;
293 : }
294 :
295 : return 0;
296 : }
297 :
298 : /* The callback was not set. Proceed.
299 : */
300 :
301 9 : if (cred->password_file == NULL) {
302 0 : gnutls_assert();
303 0 : ret = GNUTLS_E_SRP_PWD_ERROR;
304 0 : goto cleanup;
305 : }
306 :
307 : /* Open the selected password file.
308 : */
309 9 : fp = fopen(cred->password_file, "re");
310 9 : if (fp == NULL) {
311 0 : gnutls_assert();
312 0 : ret = GNUTLS_E_SRP_PWD_ERROR;
313 0 : goto cleanup;
314 : }
315 :
316 9 : len = strlen(username);
317 24 : while (getline(&line, &line_size, fp) > 0) {
318 : /* move to first ':' */
319 : i = 0;
320 135 : while ((i < line_size) && (line[i] != '\0')
321 135 : && (line[i] != ':')) {
322 111 : i++;
323 : }
324 :
325 24 : if (strncmp(username, line, MAX(i, len)) == 0) {
326 9 : if ((idx = parse_tpasswd_values(entry, line)) >= 0) {
327 : /* Keep the last index in memory, so we can retrieve fake parameters (g,n)
328 : * when the user does not exist.
329 : */
330 9 : if (pwd_read_conf
331 9 : (cred->password_conf_file, entry,
332 : idx) == 0) {
333 9 : ret = 0;
334 9 : goto found;
335 : } else {
336 0 : gnutls_assert();
337 0 : ret = GNUTLS_E_SRP_PWD_ERROR;
338 0 : goto cleanup;
339 : }
340 : } else {
341 0 : gnutls_assert();
342 0 : ret = GNUTLS_E_SRP_PWD_ERROR;
343 0 : goto cleanup;
344 : }
345 : }
346 : }
347 :
348 : /* user was not found. Fake him. Actually read the g,n values from
349 : * the last index found and randomize the entry.
350 : */
351 0 : if (pwd_read_conf(cred->password_conf_file, entry, 1) == 0) {
352 0 : ret = _randomize_pwd_entry(entry, cred, username);
353 0 : if (ret < 0) {
354 0 : gnutls_assert();
355 0 : goto cleanup;
356 : }
357 :
358 0 : ret = 0;
359 0 : goto found;
360 : }
361 :
362 : ret = GNUTLS_E_SRP_PWD_ERROR;
363 :
364 0 : cleanup:
365 0 : gnutls_assert();
366 0 : _gnutls_srp_entry_free(entry);
367 :
368 9 : found:
369 9 : if (line) {
370 9 : zeroize_key(line, line_size);
371 9 : free(line);
372 : }
373 9 : if (fp)
374 9 : fclose(fp);
375 : return ret;
376 : }
377 :
378 : /* Randomizes the given password entry. It actually sets the verifier
379 : * to random data and sets the salt based on fake_salt_seed and
380 : * username. Returns 0 on success.
381 : */
382 0 : static int _randomize_pwd_entry(SRP_PWD_ENTRY * entry,
383 : gnutls_srp_server_credentials_t sc,
384 : const char * username)
385 : {
386 0 : int ret;
387 0 : const mac_entry_st *me = mac_to_entry(SRP_FAKE_SALT_MAC);
388 0 : mac_hd_st ctx;
389 0 : size_t username_len = strlen(username);
390 :
391 0 : if (entry->g.size == 0 || entry->n.size == 0) {
392 0 : gnutls_assert();
393 0 : return GNUTLS_E_INTERNAL_ERROR;
394 : }
395 :
396 0 : entry->v.data = gnutls_malloc(20);
397 0 : entry->v.size = 20;
398 0 : if (entry->v.data == NULL) {
399 0 : gnutls_assert();
400 0 : return GNUTLS_E_MEMORY_ERROR;
401 : }
402 :
403 0 : ret = gnutls_rnd(GNUTLS_RND_NONCE, entry->v.data, 20);
404 0 : if (ret < 0) {
405 0 : gnutls_assert();
406 0 : return ret;
407 : }
408 :
409 : /* Always allocate and work with the output size of the MAC,
410 : * even if they don't need salts that long, for convenience.
411 : *
412 : * In case an error occurs 'entry' (and the salt inside)
413 : * is deallocated by our caller: _gnutls_srp_pwd_read_entry().
414 : */
415 0 : entry->salt.data = gnutls_malloc(me->output_size);
416 0 : if (entry->salt.data == NULL) {
417 0 : gnutls_assert();
418 0 : return GNUTLS_E_MEMORY_ERROR;
419 : }
420 :
421 0 : ret = _gnutls_mac_init(&ctx, me, sc->fake_salt_seed,
422 0 : sc->fake_salt_seed_size);
423 :
424 0 : if (ret < 0) {
425 0 : gnutls_assert();
426 0 : return ret;
427 : }
428 :
429 0 : _gnutls_mac(&ctx, "salt", 4);
430 0 : _gnutls_mac(&ctx, username, username_len);
431 0 : _gnutls_mac_deinit(&ctx, entry->salt.data);
432 :
433 : /* Set length to the actual number of bytes they asked for.
434 : * This is always less than or equal to the output size of
435 : * the MAC, enforced by gnutls_srp_set_server_fake_salt_seed().
436 : */
437 0 : entry->salt.size = sc->fake_salt_length;
438 :
439 0 : return 0;
440 : }
441 :
442 : /* Free all the entry parameters, except if g and n are
443 : * the static ones defined in gnutls.h
444 : */
445 174 : void _gnutls_srp_entry_free(SRP_PWD_ENTRY * entry)
446 : {
447 174 : _gnutls_free_key_datum(&entry->v);
448 174 : _gnutls_free_datum(&entry->salt);
449 :
450 174 : if ((entry->g.data != gnutls_srp_1024_group_generator.data) &&
451 174 : (entry->g.data != gnutls_srp_1536_group_generator.data) &&
452 174 : (entry->g.data != gnutls_srp_2048_group_generator.data) &&
453 174 : (entry->g.data != gnutls_srp_3072_group_generator.data) &&
454 174 : (entry->g.data != gnutls_srp_4096_group_generator.data) &&
455 174 : (entry->g.data != gnutls_srp_8192_group_generator.data))
456 174 : _gnutls_free_datum(&entry->g);
457 :
458 174 : if (entry->n.data != gnutls_srp_1024_group_prime.data &&
459 174 : entry->n.data != gnutls_srp_1536_group_prime.data &&
460 174 : entry->n.data != gnutls_srp_2048_group_prime.data &&
461 174 : entry->n.data != gnutls_srp_3072_group_prime.data &&
462 174 : entry->n.data != gnutls_srp_4096_group_prime.data &&
463 174 : entry->n.data != gnutls_srp_8192_group_prime.data)
464 174 : _gnutls_free_datum(&entry->n);
465 :
466 174 : gnutls_free(entry->username);
467 174 : gnutls_free(entry);
468 174 : }
469 :
470 : #endif /* ENABLE SRP */
|