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