/* * Copyright (C) 2001-2012 Free Software Foundation, Inc. * * Author: Nikos Mavrogiannopoulos * * This file is part of GnuTLS. * * The GnuTLS is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ /* Functions for operating in an SRP passwd file are included here */ #include "gnutls_int.h" #ifdef ENABLE_SRP #include "x509_b64.h" #include "errors.h" #include #include #include "auth.h" #include "srp.h" #include "dh.h" #include "debug.h" #include #include #include #include #include static int _randomize_pwd_entry(SRP_PWD_ENTRY *entry, gnutls_srp_server_credentials_t cred, const char *username); /* this function parses tpasswd.conf file. Format is: * string(username):base64(v):base64(salt):int(index) */ static int parse_tpasswd_values(SRP_PWD_ENTRY *entry, char *str) { char *p; int len, ret; uint8_t *verifier; size_t verifier_size; int indx; p = strrchr(str, ':'); /* we have index */ if (p == NULL) { gnutls_assert(); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } *p = '\0'; p++; indx = atoi(p); if (indx == 0) { gnutls_assert(); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } /* now go for salt */ p = strrchr(str, ':'); /* we have salt */ if (p == NULL) { gnutls_assert(); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } *p = '\0'; p++; len = strlen(p); entry->salt.size = _gnutls_sbase64_decode(p, len, &entry->salt.data); if (entry->salt.size <= 0) { gnutls_assert(); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } /* now go for verifier */ p = strrchr(str, ':'); /* we have verifier */ if (p == NULL) { _gnutls_free_datum(&entry->salt); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } *p = '\0'; p++; len = strlen(p); ret = _gnutls_sbase64_decode(p, len, &verifier); if (ret <= 0) { gnutls_assert(); _gnutls_free_datum(&entry->salt); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } verifier_size = ret; entry->v.data = verifier; entry->v.size = verifier_size; /* now go for username */ *p = '\0'; entry->username = gnutls_strdup(str); if (entry->username == NULL) { _gnutls_free_datum(&entry->salt); _gnutls_free_key_datum(&entry->v); gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } return indx; } /* this function parses tpasswd.conf file. Format is: * int(index):base64(n):int(g) */ static int parse_tpasswd_conf_values(SRP_PWD_ENTRY *entry, char *str) { char *p; int len; uint8_t *tmp; int ret; p = strrchr(str, ':'); /* we have g */ if (p == NULL) { gnutls_assert(); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } *p = '\0'; p++; /* read the generator */ len = strlen(p); if (p[len - 1] == '\n' || p[len - 1] == ' ') len--; ret = _gnutls_sbase64_decode(p, len, &tmp); if (ret < 0) { gnutls_assert(); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } entry->g.data = tmp; entry->g.size = ret; /* now go for n - modulo */ p = strrchr(str, ':'); /* we have n */ if (p == NULL) { _gnutls_free_datum(&entry->g); gnutls_assert(); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } *p = '\0'; p++; len = strlen(p); ret = _gnutls_sbase64_decode(p, len, &tmp); if (ret < 0) { gnutls_assert(); _gnutls_free_datum(&entry->g); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } entry->n.data = tmp; entry->n.size = ret; return 0; } /* this function opens the tpasswd.conf file and reads the g and n * values. They are put in the entry. */ static int pwd_read_conf(const char *pconf_file, SRP_PWD_ENTRY *entry, int idx) { FILE *fp; char *line = NULL; size_t line_size = 0; unsigned i, len; char indexstr[10]; int ret; snprintf(indexstr, sizeof(indexstr), "%u", (unsigned int)idx); fp = fopen(pconf_file, "re"); if (fp == NULL) { gnutls_assert(); return GNUTLS_E_FILE_ERROR; } len = strlen(indexstr); while (getline(&line, &line_size, fp) > 0) { /* move to first ':' */ i = 0; while ((i < line_size) && (line[i] != ':') && (line[i] != '\0')) { i++; } if (strncmp(indexstr, line, MAX(i, len)) == 0) { if (parse_tpasswd_conf_values(entry, line) >= 0) { ret = 0; goto cleanup; } else { ret = GNUTLS_E_SRP_PWD_ERROR; goto cleanup; } } } ret = GNUTLS_E_SRP_PWD_ERROR; cleanup: zeroize_key(line, line_size); free(line); fclose(fp); return ret; } int _gnutls_srp_pwd_read_entry(gnutls_session_t state, char *username, SRP_PWD_ENTRY **_entry) { gnutls_srp_server_credentials_t cred; FILE *fp = NULL; char *line = NULL; size_t line_size = 0; unsigned i, len; int ret; int idx; SRP_PWD_ENTRY *entry = NULL; *_entry = gnutls_calloc(1, sizeof(SRP_PWD_ENTRY)); if (*_entry == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } entry = *_entry; cred = (gnutls_srp_server_credentials_t)_gnutls_get_cred( state, GNUTLS_CRD_SRP); if (cred == NULL) { gnutls_assert(); ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS; goto cleanup; } /* if the callback which sends the parameters is * set, use it. */ if (cred->pwd_callback != NULL) { ret = cred->pwd_callback(state, username, &entry->salt, &entry->v, &entry->g, &entry->n); if (ret == 1) { /* the user does not exist */ if (entry->g.size != 0 && entry->n.size != 0) { ret = _randomize_pwd_entry(entry, cred, username); if (ret < 0) { gnutls_assert(); goto cleanup; } return 0; } else { gnutls_assert(); ret = -1; /* error in the callback */ } } if (ret < 0) { gnutls_assert(); ret = GNUTLS_E_SRP_PWD_ERROR; goto cleanup; } return 0; } /* The callback was not set. Proceed. */ if (cred->password_file == NULL) { gnutls_assert(); ret = GNUTLS_E_SRP_PWD_ERROR; goto cleanup; } /* Open the selected password file. */ fp = fopen(cred->password_file, "re"); if (fp == NULL) { gnutls_assert(); ret = GNUTLS_E_SRP_PWD_ERROR; goto cleanup; } len = strlen(username); while (getline(&line, &line_size, fp) > 0) { /* move to first ':' */ i = 0; while ((i < line_size) && (line[i] != '\0') && (line[i] != ':')) { i++; } if (strncmp(username, line, MAX(i, len)) == 0) { if ((idx = parse_tpasswd_values(entry, line)) >= 0) { /* Keep the last index in memory, so we can retrieve fake parameters (g,n) * when the user does not exist. */ if (pwd_read_conf(cred->password_conf_file, entry, idx) == 0) { ret = 0; goto found; } else { gnutls_assert(); ret = GNUTLS_E_SRP_PWD_ERROR; goto cleanup; } } else { gnutls_assert(); ret = GNUTLS_E_SRP_PWD_ERROR; goto cleanup; } } } /* user was not found. Fake him. Actually read the g,n values from * the last index found and randomize the entry. */ if (pwd_read_conf(cred->password_conf_file, entry, 1) == 0) { ret = _randomize_pwd_entry(entry, cred, username); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = 0; goto found; } ret = GNUTLS_E_SRP_PWD_ERROR; cleanup: gnutls_assert(); _gnutls_srp_entry_free(entry); found: if (line) { zeroize_key(line, line_size); free(line); } if (fp) fclose(fp); return ret; } /* Randomizes the given password entry. It actually sets the verifier * to random data and sets the salt based on fake_salt_seed and * username. Returns 0 on success. */ static int _randomize_pwd_entry(SRP_PWD_ENTRY *entry, gnutls_srp_server_credentials_t sc, const char *username) { int ret; const mac_entry_st *me = mac_to_entry(SRP_FAKE_SALT_MAC); mac_hd_st ctx; size_t username_len = strlen(username); if (entry->g.size == 0 || entry->n.size == 0) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } entry->v.data = gnutls_malloc(20); entry->v.size = 20; if (entry->v.data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } ret = gnutls_rnd(GNUTLS_RND_NONCE, entry->v.data, 20); if (ret < 0) { gnutls_assert(); return ret; } /* Always allocate and work with the output size of the MAC, * even if they don't need salts that long, for convenience. * * In case an error occurs 'entry' (and the salt inside) * is deallocated by our caller: _gnutls_srp_pwd_read_entry(). */ entry->salt.data = gnutls_malloc(me->output_size); if (entry->salt.data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } ret = _gnutls_mac_init(&ctx, me, sc->fake_salt_seed, sc->fake_salt_seed_size); if (ret < 0) { gnutls_assert(); return ret; } _gnutls_mac(&ctx, "salt", 4); _gnutls_mac(&ctx, username, username_len); _gnutls_mac_deinit(&ctx, entry->salt.data); /* Set length to the actual number of bytes they asked for. * This is always less than or equal to the output size of * the MAC, enforced by gnutls_srp_set_server_fake_salt_seed(). */ entry->salt.size = sc->fake_salt_length; return 0; } /* Free all the entry parameters, except if g and n are * the static ones defined in gnutls.h */ void _gnutls_srp_entry_free(SRP_PWD_ENTRY *entry) { _gnutls_free_key_datum(&entry->v); _gnutls_free_datum(&entry->salt); if ((entry->g.data != gnutls_srp_1024_group_generator.data) && (entry->g.data != gnutls_srp_1536_group_generator.data) && (entry->g.data != gnutls_srp_2048_group_generator.data) && (entry->g.data != gnutls_srp_3072_group_generator.data) && (entry->g.data != gnutls_srp_4096_group_generator.data) && (entry->g.data != gnutls_srp_8192_group_generator.data)) _gnutls_free_datum(&entry->g); if (entry->n.data != gnutls_srp_1024_group_prime.data && entry->n.data != gnutls_srp_1536_group_prime.data && entry->n.data != gnutls_srp_2048_group_prime.data && entry->n.data != gnutls_srp_3072_group_prime.data && entry->n.data != gnutls_srp_4096_group_prime.data && entry->n.data != gnutls_srp_8192_group_prime.data) _gnutls_free_datum(&entry->n); gnutls_free(entry->username); gnutls_free(entry); } #endif /* ENABLE SRP */