/* * Copyright (C) 2005-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 PSK passwd file are included here */ #include "gnutls_int.h" #include "x509_b64.h" #include "errors.h" #include #include #include "auth.h" #include "dh.h" #include "debug.h" #include #include #include #include /* this function parses passwd.psk file. Format is: * string(username):hex(passwd) */ static int pwd_put_values(gnutls_datum_t * psk, char *str) { char *p; int len, ret; gnutls_datum_t tmp; p = strchr(str, ':'); if (p == NULL) { gnutls_assert(); return GNUTLS_E_SRP_PWD_PARSING_ERROR; } *p = '\0'; p++; /* skip username */ /* read the key */ len = strlen(p); if (p[len - 1] == '\n' || p[len - 1] == ' ') len--; tmp.data = (void*)p; tmp.size = len; ret = gnutls_hex_decode2(&tmp, psk); if (ret < 0) { gnutls_assert(); return ret; } return 0; } static bool username_matches(const gnutls_datum_t *username, const char *line, size_t line_size) { int retval; unsigned i; gnutls_datum_t hexline, hex_username = { NULL, 0 }; /* * Guard against weird behavior - we don't check 'line', * as it's returned by getline(), which will never return NULL * if successful. */ if (username->data == NULL) return false; if (line_size == 0) return (username->size == 0); /* move to first ':' */ i = 0; while ((i < line_size) && (line[i] != '\0') && (line[i] != ':')) { i++; } /* if format is in hex, e.g. #FAFAFA */ if (line[0] == '#' && line_size > 1) { hexline.data = (void *) &line[1]; hexline.size = i - 1; if (gnutls_hex_decode2(&hexline, &hex_username) < 0) return gnutls_assert_val(0); if (hex_username.size == username->size) retval = memcmp(username->data, hex_username.data, username->size); else retval = -1; _gnutls_free_datum(&hex_username); } else { retval = strncmp((const char *) username->data, line, MAX(i, username->size)); } return (retval == 0); } /* Randomizes the given password entry. It actually sets a random password. * Returns 0 on success. */ static int _randomize_psk(gnutls_datum_t * psk) { int ret; psk->data = gnutls_malloc(16); if (psk->data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } psk->size = 16; ret = gnutls_rnd(GNUTLS_RND_NONCE, (char *) psk->data, 16); if (ret < 0) { gnutls_assert(); return ret; } return 0; } /* Returns the PSK key of the given user. * If the user doesn't exist a random password is returned instead. */ int _gnutls_psk_pwd_find_entry(gnutls_session_t session, const char *username, uint16_t username_len, gnutls_datum_t * psk) { gnutls_psk_server_credentials_t cred; FILE *fp; char *line = NULL; size_t line_size = 0; int ret; gnutls_datum_t username_datum = { .data = (unsigned char *) username, .size = username_len }; cred = (gnutls_psk_server_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_PSK); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } /* if the callback which sends the parameters is * set, use it. */ if (cred->pwd_callback != NULL) { ret = cred->pwd_callback(session, &username_datum, psk); if (ret == 1) { /* the user does not exist */ ret = _randomize_psk(psk); if (ret < 0) { gnutls_assert(); return ret; } return 0; } if (ret < 0) { gnutls_assert(); return GNUTLS_E_SRP_PWD_ERROR; } return 0; } /* The callback was not set. Proceed. */ if (cred->password_file == NULL) { gnutls_assert(); return GNUTLS_E_SRP_PWD_ERROR; } /* Open the selected password file. */ fp = fopen(cred->password_file, "re"); if (fp == NULL) { gnutls_assert(); return GNUTLS_E_SRP_PWD_ERROR; } while (getline(&line, &line_size, fp) > 0) { if (username_matches(&username_datum, line, line_size)) { ret = pwd_put_values(psk, line); if (ret < 0) { gnutls_assert(); ret = GNUTLS_E_SRP_PWD_ERROR; goto cleanup; } ret = 0; goto cleanup; } } /* user was not found. Fake him. */ ret = _randomize_psk(psk); if (ret < 0) { goto cleanup; } ret = 0; cleanup: if (fp != NULL) fclose(fp); zeroize_key(line, line_size); free(line); return ret; } /* returns the username and they key for the PSK session. * Free is non (0) if they have to be freed. */ int _gnutls_find_psk_key(gnutls_session_t session, gnutls_psk_client_credentials_t cred, gnutls_datum_t * username, gnutls_datum_t * key, int *free) { int ret; *free = 0; if (cred->username.data != NULL && cred->key.data != NULL) { username->data = cred->username.data; username->size = cred->username.size; key->data = cred->key.data; key->size = cred->key.size; } else if (cred->get_function != NULL) { ret = cred->get_function(session, username, key); if (ret) return gnutls_assert_val(ret); *free = 1; } else return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); return 0; }