summaryrefslogtreecommitdiffstats
path: root/lib/auth/psk_passwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/auth/psk_passwd.c')
-rw-r--r--lib/auth/psk_passwd.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/lib/auth/psk_passwd.c b/lib/auth/psk_passwd.c
new file mode 100644
index 0000000..2953c2d
--- /dev/null
+++ b/lib/auth/psk_passwd.c
@@ -0,0 +1,273 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions for operating in an PSK passwd file are included here */
+
+#include "gnutls_int.h"
+
+#include "x509_b64.h"
+#include "errors.h"
+#include <auth/psk_passwd.h>
+#include <auth/psk.h>
+#include "auth.h"
+#include "dh.h"
+#include "debug.h"
+#include <str.h>
+#include <datum.h>
+#include <num.h>
+#include <random.h>
+
+
+/* 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;
+}