1e21fb479STrond Norbye/*
2e21fb479STrond Norbye *     Copyright 2013 Couchbase, Inc.
3e21fb479STrond Norbye *
4e21fb479STrond Norbye *   Licensed under the Apache License, Version 2.0 (the "License");
5e21fb479STrond Norbye *   you may not use this file except in compliance with the License.
6e21fb479STrond Norbye *   You may obtain a copy of the License at
7e21fb479STrond Norbye *
8e21fb479STrond Norbye *       http://www.apache.org/licenses/LICENSE-2.0
9e21fb479STrond Norbye *
10e21fb479STrond Norbye *   Unless required by applicable law or agreed to in writing, software
11e21fb479STrond Norbye *   distributed under the License is distributed on an "AS IS" BASIS,
12e21fb479STrond Norbye *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e21fb479STrond Norbye *   See the License for the specific language governing permissions and
14e21fb479STrond Norbye *   limitations under the License.
15e21fb479STrond Norbye */
16e21fb479STrond Norbye#include "cram-md5.h"
17e21fb479STrond Norbye#include "hmac.h"
18e21fb479STrond Norbye#include "cbsasl/pwfile.h"
19e21fb479STrond Norbye#include "cbsasl/util.h"
20e21fb479STrond Norbye#include <string.h>
21e21fb479STrond Norbye#include <stdlib.h>
22e21fb479STrond Norbye
23e21fb479STrond Norbye#define NONCE_LENGTH 8
24e21fb479STrond Norbye
25e21fb479STrond Norbyecbsasl_error_t cram_md5_server_init() {
26e21fb479STrond Norbye    return SASL_OK;
27e21fb479STrond Norbye}
28e21fb479STrond Norbye
29e21fb479STrond Norbyecbsasl_error_t cram_md5_server_start(cbsasl_conn_t* conn) {
30e21fb479STrond Norbye    /* Generate a challenge */
31e21fb479STrond Norbye    char nonce[NONCE_LENGTH];
32e21fb479STrond Norbye    if (cbsasl_secure_random(nonce, NONCE_LENGTH) != SASL_OK) {
33e21fb479STrond Norbye        return SASL_FAIL;
34e21fb479STrond Norbye    }
35e21fb479STrond Norbye
36e21fb479STrond Norbye    conn->c.server.sasl_data = malloc(NONCE_LENGTH * 2);
37e21fb479STrond Norbye    if(conn->c.server.sasl_data == NULL) {
38e21fb479STrond Norbye        return SASL_FAIL;
39e21fb479STrond Norbye    }
40e21fb479STrond Norbye
41e21fb479STrond Norbye    conn->c.server.sasl_data_len = NONCE_LENGTH * 2;
42e21fb479STrond Norbye    cbsasl_hex_encode(conn->c.server.sasl_data, nonce, NONCE_LENGTH);
43e21fb479STrond Norbye    return SASL_CONTINUE;
44e21fb479STrond Norbye}
45e21fb479STrond Norbye
46e21fb479STrond Norbyecbsasl_error_t cram_md5_server_step(cbsasl_conn_t *conn,
47e21fb479STrond Norbye                                    const char *input,
48e21fb479STrond Norbye                                    unsigned inputlen,
49e21fb479STrond Norbye                                    const char **output,
50e21fb479STrond Norbye                                    unsigned *outputlen)
51e21fb479STrond Norbye{
52e21fb479STrond Norbye    unsigned int userlen;
53e21fb479STrond Norbye    char *user;
54e21fb479STrond Norbye    char *cfg;
55e21fb479STrond Norbye    char *pass;
56e21fb479STrond Norbye    unsigned char digest[DIGEST_LENGTH];
57e21fb479STrond Norbye    char md5string[DIGEST_LENGTH * 2];
58e21fb479STrond Norbye
59e21fb479STrond Norbye    if (inputlen <= 33) {
60e21fb479STrond Norbye        return SASL_BADPARAM;
61e21fb479STrond Norbye    }
62e21fb479STrond Norbye
63e21fb479STrond Norbye    userlen = inputlen - (DIGEST_LENGTH * 2) - 1;
64e21fb479STrond Norbye    user = calloc((userlen + 1) * sizeof(char), 1);
65e21fb479STrond Norbye    memcpy(user, input, userlen);
66e21fb479STrond Norbye    user[userlen] = '\0';
67ca4ab0eaSTrond Norbye    conn->c.server.username = user;
68e21fb479STrond Norbye
69e21fb479STrond Norbye    pass = find_pw(user, &cfg);
70e21fb479STrond Norbye    if (pass == NULL) {
71ca4ab0eaSTrond Norbye        return SASL_NOUSER;
72e21fb479STrond Norbye    }
73e21fb479STrond Norbye
74e21fb479STrond Norbye    hmac_md5((unsigned char *)conn->c.server.sasl_data,
75e21fb479STrond Norbye             conn->c.server.sasl_data_len,
76e21fb479STrond Norbye             (unsigned char *)pass,
77e21fb479STrond Norbye             strlen(pass), digest);
78e21fb479STrond Norbye
79e21fb479STrond Norbye    cbsasl_hex_encode(md5string, (char *) digest, DIGEST_LENGTH);
80e21fb479STrond Norbye
81e21fb479STrond Norbye    if (cbsasl_secure_compare(md5string,
82e21fb479STrond Norbye                              (DIGEST_LENGTH * 2),
83e21fb479STrond Norbye                              &(input[userlen + 1]),
84e21fb479STrond Norbye                              (DIGEST_LENGTH * 2)) != 0) {
85ca4ab0eaSTrond Norbye        return SASL_PWERR;
86e21fb479STrond Norbye    }
87e21fb479STrond Norbye
88e21fb479STrond Norbye    conn->c.server.config = strdup(cfg);
89e21fb479STrond Norbye    *output = NULL;
90e21fb479STrond Norbye    *outputlen = 0;
91e21fb479STrond Norbye    return SASL_OK;
92e21fb479STrond Norbye}
93e21fb479STrond Norbye
94e21fb479STrond Norbyecbsasl_mechs_t get_cram_md5_mechs(void)
95e21fb479STrond Norbye{
96e21fb479STrond Norbye    static cbsasl_mechs_t mechs = {
97e21fb479STrond Norbye        MECH_NAME_CRAM_MD5,
98e21fb479STrond Norbye        cram_md5_server_init,
99e21fb479STrond Norbye        cram_md5_server_start,
100e21fb479STrond Norbye        cram_md5_server_step
101e21fb479STrond Norbye    };
102e21fb479STrond Norbye    return mechs;
103e21fb479STrond Norbye}