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 
cram_md5_server_initnull25 cbsasl_error_t cram_md5_server_init() {
26     return SASL_OK;
27 }
28 
cram_md5_server_start(cbsasl_conn_t* conn)29 cbsasl_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 
cram_md5_server_step(cbsasl_conn_t *conn, const char *input, unsigned inputlen, const char **output, unsigned *outputlen)46 cbsasl_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 
get_cram_md5_mechs(void)94 cbsasl_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