/* Send the first key exchange message ( g, n, s) and append the verifier algorithm number * Data is allocated by the caller, and should have data_size size. */ int _gnutls_gen_srp_server_kx (gnutls_session_t session, opaque ** data) { int ret; uint8_t *data_n, *data_s; uint8_t *data_g; char *username; SRP_PWD_ENTRY *pwd_entry; srp_server_auth_info_t info; ssize_t data_size; size_t n_b, tmp_size; char buf[64]; uint8_t *data_b; if ((ret = _gnutls_auth_info_set (session, GNUTLS_CRD_SRP, sizeof (srp_server_auth_info_st), 1)) < 0) { gnutls_assert (); return ret; } info = _gnutls_get_auth_info (session); username = info->username; _gnutls_str_cpy (username, MAX_SRP_USERNAME, session->security_parameters.extensions.srp_username); ret = _gnutls_srp_pwd_read_entry (session, username, &pwd_entry); if (ret < 0) { gnutls_assert (); return ret; } /* copy from pwd_entry to local variables (actually in session) */ tmp_size = pwd_entry->g.size; if (_gnutls_mpi_scan_nz (&G, pwd_entry->g.data, &tmp_size) < 0) { gnutls_assert (); return GNUTLS_E_MPI_SCAN_FAILED; } tmp_size = pwd_entry->n.size; if (_gnutls_mpi_scan_nz (&N, pwd_entry->n.data, &tmp_size) < 0) { gnutls_assert (); return GNUTLS_E_MPI_SCAN_FAILED; } tmp_size = pwd_entry->v.size; if (_gnutls_mpi_scan_nz (&V, pwd_entry->v.data, &tmp_size) < 0) { gnutls_assert (); return GNUTLS_E_MPI_SCAN_FAILED; } /* Calculate: B = (k*v + g^b) % N */ B = _gnutls_calc_srp_B (&_b, G, N, V); if (B == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } if (_gnutls_mpi_print (NULL, &n_b, B) != 0) { gnutls_assert (); return GNUTLS_E_MPI_PRINT_FAILED; } /* Allocate size to hold the N, g, s, B */ data_size = (pwd_entry->n.size + 2 + pwd_entry->g.size + 2 + pwd_entry->salt.size + 1) + (n_b + 2); (*data) = gnutls_malloc (data_size); if ((*data) == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } /* copy N (mod n) */ data_n = *data; _gnutls_write_datum16 (data_n, pwd_entry->n); /* copy G (generator) to data */ data_g = &data_n[2 + pwd_entry->n.size]; _gnutls_write_datum16 (data_g, pwd_entry->g); /* copy the salt */ data_s = &data_g[2 + pwd_entry->g.size]; _gnutls_write_datum8 (data_s, pwd_entry->salt); /* Copy the B value */ data_b = &data_s[1 + pwd_entry->salt.size]; if (_gnutls_mpi_print (&data_b[2], &n_b, B) != 0) { gnutls_assert(); return GNUTLS_E_MPI_PRINT_FAILED; } _gnutls_write_uint16 (n_b, data_b); _gnutls_hard_log ("INT: SRP B[%d]: %s\n", n_b, _gnutls_bin2hex (&data_b[2], n_b, buf, sizeof (buf))); _gnutls_srp_entry_free (pwd_entry); return data_size; }
int _gnutls_srp_pwd_read_entry (gnutls_session_t state, char *username, SRP_PWD_ENTRY ** _entry) { gnutls_srp_server_credentials_t cred; FILE *fd; char line[2 * 1024]; unsigned i, len; int ret; int idx, last_idx; SRP_PWD_ENTRY *entry; *_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->key, GNUTLS_CRD_SRP, NULL); if (cred == NULL) { gnutls_assert (); _gnutls_srp_entry_free (entry); 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 (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); if (ret < 0) { gnutls_assert (); _gnutls_srp_entry_free (entry); return ret; } return 0; } else { gnutls_assert (); ret = -1; /* error in the callback */ } } if (ret < 0) { gnutls_assert (); _gnutls_srp_entry_free (entry); 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. */ fd = fopen (cred->password_file, "r"); if (fd == NULL) { gnutls_assert (); _gnutls_srp_entry_free (entry); return GNUTLS_E_SRP_PWD_ERROR; } last_idx = 1; /* a default value */ len = strlen (username); while (fgets (line, sizeof (line), fd) != NULL) { /* move to first ':' */ i = 0; while ((line[i] != ':') && (line[i] != '\0') && (i < sizeof (line))) { i++; } if (strncmp (username, line, MAX (i, len)) == 0) { if ((idx = pwd_put_values (entry, line)) >= 0) { /* Keep the last index in memory, so we can retrieve fake parameters (g,n) * when the user does not exist. */ /* XXX: last_idx will not be read as both if block branches return. */ last_idx = idx; if (pwd_read_conf (cred->password_conf_file, entry, idx) == 0) { return 0; } else { gnutls_assert (); _gnutls_srp_entry_free (entry); return GNUTLS_E_SRP_PWD_ERROR; } } else { gnutls_assert (); _gnutls_srp_entry_free (entry); return GNUTLS_E_SRP_PWD_ERROR; } } } /* 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, last_idx) == 0) { ret = _randomize_pwd_entry (entry); if (ret < 0) { gnutls_assert (); _gnutls_srp_entry_free (entry); return ret; } return 0; } gnutls_assert (); _gnutls_srp_entry_free (entry); return GNUTLS_E_SRP_PWD_ERROR; }
/* Send the first key exchange message ( g, n, s) and append the verifier algorithm number * Data is allocated by the caller, and should have data_size size. */ int _gnutls_gen_srp_server_kx(gnutls_session_t session, gnutls_buffer_st * data) { int ret; char *username; SRP_PWD_ENTRY *pwd_entry; srp_server_auth_info_t info; size_t tmp_size; extension_priv_data_t epriv; srp_ext_st *priv; ret = _gnutls_ext_get_session_data(session, GNUTLS_EXTENSION_SRP, &epriv); if (ret < 0) { /* peer didn't send a username */ gnutls_assert(); return GNUTLS_E_UNKNOWN_SRP_USERNAME; } priv = epriv; if ((ret = _gnutls_auth_info_set(session, GNUTLS_CRD_SRP, sizeof(srp_server_auth_info_st), 1)) < 0) { gnutls_assert(); return ret; } info = _gnutls_get_auth_info(session, GNUTLS_CRD_SRP); if (info == NULL) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); username = info->username; _gnutls_str_cpy(username, MAX_USERNAME_SIZE, priv->username); ret = _gnutls_srp_pwd_read_entry(session, username, &pwd_entry); if (ret < 0) { gnutls_assert(); return ret; } /* copy from pwd_entry to local variables (actually in session) */ tmp_size = pwd_entry->g.size; if (_gnutls_mpi_init_scan_nz(&G, pwd_entry->g.data, tmp_size) < 0) { gnutls_assert(); ret = GNUTLS_E_MPI_SCAN_FAILED; goto cleanup; } tmp_size = pwd_entry->n.size; if (_gnutls_mpi_init_scan_nz(&N, pwd_entry->n.data, tmp_size) < 0) { gnutls_assert(); ret = GNUTLS_E_MPI_SCAN_FAILED; goto cleanup; } tmp_size = pwd_entry->v.size; if (_gnutls_mpi_init_scan_nz(&V, pwd_entry->v.data, tmp_size) < 0) { gnutls_assert(); ret = GNUTLS_E_MPI_SCAN_FAILED; goto cleanup; } /* Calculate: B = (k*v + g^b) % N */ B = _gnutls_calc_srp_B(&_b, G, N, V); if (B == NULL) { gnutls_assert(); ret = GNUTLS_E_MEMORY_ERROR; goto cleanup; } /* copy N (mod n) */ ret = _gnutls_buffer_append_data_prefix(data, 16, pwd_entry->n.data, pwd_entry->n.size); if (ret < 0) { gnutls_assert(); goto cleanup; } /* copy G (generator) to data */ ret = _gnutls_buffer_append_data_prefix(data, 16, pwd_entry->g.data, pwd_entry->g.size); if (ret < 0) { gnutls_assert(); goto cleanup; } /* copy the salt */ ret = _gnutls_buffer_append_data_prefix(data, 8, pwd_entry->salt.data, pwd_entry->salt.size); if (ret < 0) { gnutls_assert(); goto cleanup; } /* Copy the B value */ ret = _gnutls_buffer_append_mpi(data, 16, B, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } _gnutls_mpi_log("SRP B: ", B); ret = data->length; cleanup: _gnutls_srp_entry_free(pwd_entry); 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 *fd = 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. */ fd = fopen(cred->password_file, "r"); if (fd == NULL) { gnutls_assert(); ret = GNUTLS_E_SRP_PWD_ERROR; goto cleanup; } len = strlen(username); while (getline(&line, &line_size, fd) > 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: zeroize_key(line, line_size); free(line); if (fd) fclose(fd); return ret; }