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 <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <platform/platform.h>
20#include <cbsasl/cbsasl.h>
21#include "cbsasl/cram-md5/hmac.h"
22#include "cbsasl/pwfile.h"
23#include "cbsasl/util.h"
24
25const char *cbpwfile = "sasl_server_test.pw";
26
27static void create_pw_file()
28{
29    FILE *fp = fopen(cbpwfile, "w");
30    cb_assert(fp != NULL);
31
32    fprintf(fp, "mikewied mikepw \ncseo cpw \njlim jpw \nnopass\n");
33    cb_assert(fclose(fp) == 0);
34
35    putenv("ISASL_PWFILE=sasl_server_test.pw");
36}
37
38static void remove_pw_file()
39{
40    cb_assert(remove(cbpwfile) == 0);
41    free_user_ht();
42}
43
44static void construct_cram_md5_credentials(char *buffer,
45                                           unsigned *bufferlen,
46                                           const char *user,
47                                           unsigned userlen,
48                                           const char *pass,
49                                           unsigned passlen,
50                                           const char *challenge,
51                                           unsigned challengelen)
52{
53    unsigned char digest[DIGEST_LENGTH];
54    memcpy(buffer, user, userlen);
55    buffer[userlen + 1] = ' ';
56
57    hmac_md5((unsigned char *)challenge, challengelen, (unsigned char *)pass, passlen, digest);
58
59    cbsasl_hex_encode(buffer + userlen + 1, (char *) digest, DIGEST_LENGTH);
60    *bufferlen = 1 + (DIGEST_LENGTH * 2) + userlen;
61}
62
63static void test_list_mechs()
64{
65    const char *mechs = NULL;
66    unsigned len = 0;
67    cbsasl_error_t err = cbsasl_list_mechs(&mechs, &len);
68    cb_assert(err == SASL_OK);
69    cb_assert(strncmp(mechs, "CRAM-MD5 PLAIN", len) == 0);
70    cb_assert(strncmp(mechs, "CRDM-MD5 PLAIN", len) != 0);
71}
72
73static void test_plain_auth()
74{
75    cbsasl_conn_t *conn = NULL;
76    const char *output = NULL;
77    unsigned outputlen = 0;
78    cbsasl_error_t err;
79
80    err = cbsasl_server_init();
81    cb_assert(err == SASL_OK);
82
83    err = cbsasl_server_start(&conn, "bad_mech", NULL, 0, NULL, NULL);
84    cb_assert(err == SASL_BADPARAM);
85    free((void *)output);
86
87    /* Normal behavior */
88    output = NULL;
89    err = cbsasl_server_start(&conn, "PLAIN", NULL, 0, NULL, NULL);
90    cb_assert(err == SASL_CONTINUE);
91
92    err = cbsasl_server_step(conn, "\0mikewied\0mikepw", 16, &output, &outputlen);
93    cb_assert(err == SASL_OK);
94    free((void *)output);
95    cbsasl_server_term();
96
97
98    /* With wrong password */
99    output = NULL;
100    err = cbsasl_server_init();
101    cb_assert(err == SASL_OK);
102
103    err = cbsasl_server_start(&conn, "PLAIN", NULL, 0, NULL, NULL);
104    cb_assert(err == SASL_CONTINUE);
105
106    err = cbsasl_server_step(conn, "\0mikewied\0badpPW", 16, &output, &outputlen);
107    cb_assert(err == SASL_PWERR);
108    free((void *)output);
109
110    cbsasl_dispose(&conn);
111    cb_assert(conn == NULL);
112
113    cbsasl_server_term();
114
115    /* with no password */
116    output = NULL;
117    err = cbsasl_server_init();
118    cb_assert(err == SASL_OK);
119
120    err = cbsasl_server_start(&conn, "PLAIN", NULL, 0, NULL, NULL);
121    cb_assert(err == SASL_CONTINUE);
122
123    err = cbsasl_server_step(conn, "\0nopass\0", 8, &output, &outputlen);
124    cb_assert(err == SASL_OK);
125    free((void *)output);
126
127    cbsasl_server_term();
128
129    /* with authzid */
130    output = NULL;
131    err = cbsasl_server_init();
132    cb_assert(err == SASL_OK);
133
134    err = cbsasl_server_start(&conn, "PLAIN", NULL, 0, NULL, NULL);
135    cb_assert(err == SASL_CONTINUE);
136
137    err = cbsasl_server_step(conn, "funzid\0mikewied\0mikepw", 22, &output, &outputlen);
138    cb_assert(err == SASL_OK);
139    free((void *)output);
140
141    cbsasl_server_term();
142
143    /* with no pw or username ending null */
144    output = NULL;
145    err = cbsasl_server_init();
146    cb_assert(err == SASL_OK);
147
148    err = cbsasl_server_start(&conn, "PLAIN", NULL, 0, NULL, NULL);
149    cb_assert(err == SASL_CONTINUE);
150
151    err = cbsasl_server_step(conn, "funzid\0mikewied", 15, &output, &outputlen);
152    cb_assert(err != SASL_OK);
153    free((void *)output);
154    cbsasl_server_term();
155
156    /* with no nulls at all */
157    output = NULL;
158    err = cbsasl_server_init();
159    cb_assert(err == SASL_OK);
160
161    err = cbsasl_server_start(&conn, "PLAIN", NULL, 0, NULL, NULL);
162    cb_assert(err == SASL_CONTINUE);
163
164    err = cbsasl_server_step(conn, "funzidmikewied", 14, &output, &outputlen);
165    cb_assert(err != SASL_OK);
166    free((void *)output);
167    cbsasl_server_term();
168
169    cbsasl_dispose(&conn);
170    cb_assert(conn == NULL);
171}
172
173static void test_cram_md5_auth()
174{
175    const char *user = "mikewied";
176    const char *pass = "mikepw";
177    cbsasl_conn_t *conn = NULL;
178    char creds[128];
179    unsigned credslen = 0;
180    const char *output = NULL;
181    unsigned outputlen = 0;
182
183    cbsasl_error_t err = cbsasl_server_init();
184    cb_assert(err == SASL_OK);
185
186    err = cbsasl_server_start(&conn, "CRAM-MD5", NULL, 0, NULL, NULL);
187    cb_assert(err == SASL_CONTINUE);
188
189    construct_cram_md5_credentials(creds, &credslen, user, strlen(user), pass,
190                                   strlen(pass), conn->c.server.sasl_data,
191                                   conn->c.server.sasl_data_len);
192
193    err = cbsasl_server_step(conn, creds, credslen, &output, &outputlen);
194    cb_assert(err == SASL_OK);
195    if (output != NULL) {
196        free((char *)output);
197    }
198
199    cbsasl_dispose(&conn);
200    cbsasl_server_term();
201    cb_assert(conn == NULL);
202}
203
204int main()
205{
206    create_pw_file();
207
208    test_list_mechs();
209    test_plain_auth();
210    test_cram_md5_auth();
211
212    remove_pw_file();
213    return 0;
214}
215