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 
17 #include <cbsasl/cbsasl.h>
18 
19 #include "cram-md5/cram-md5.h"
20 #include "cram-md5/hmac.h"
21 #include "plain/plain.h"
22 #include "pwfile.h"
23 #include "util.h"
24 #include <time.h>
25 #include <platform/random.h>
26 #include <string.h>
27 #include <stdlib.h>
28 
29 static cb_rand_t randgen;
30 
31 #define IS_MECH(str, mech) (strncmp(str, mech, strlen(mech)))
32 
cbsasl_list_mechs(const char **mechs, unsigned *mechslen)33 cbsasl_error_t cbsasl_list_mechs(const char **mechs,
34                                  unsigned *mechslen)
35 {
36     *mechs = "CRAM-MD5 PLAIN";
37     *mechslen = strlen(*mechs);
38     return SASL_OK;
39 }
40 
41 CBSASL_PUBLIC_API
cbsasl_server_init(void)42 cbsasl_error_t cbsasl_server_init(void)
43 {
44     if (cb_rand_open(&randgen) != 0) {
45         return SASL_FAIL;
46     }
47     pwfile_init();
48     return load_user_db();
49 }
50 
51 CBSASL_PUBLIC_API
cbsasl_server_term(void)52 cbsasl_error_t cbsasl_server_term(void)
53 {
54     return cb_rand_close(randgen) == 0 ? SASL_OK : SASL_FAIL;
55 
56 }
57 
58 CBSASL_PUBLIC_API
cbsasl_server_start(cbsasl_conn_t **conn, const char *mech, const char *clientin, unsigned int clientinlen, unsigned char **serverout, unsigned int *serveroutlen)59 cbsasl_error_t cbsasl_server_start(cbsasl_conn_t **conn,
60                                    const char *mech,
61                                    const char *clientin,
62                                    unsigned int clientinlen,
63                                    unsigned char **serverout,
64                                    unsigned int *serveroutlen)
65 {
66     cbsasl_error_t err;
67 
68     if (*conn != NULL) {
69         cbsasl_dispose(conn);
70     }
71 
72     *conn = calloc(1, sizeof(cbsasl_conn_t));
73     if (*conn == NULL) {
74         return SASL_NOMEM;
75     }
76 
77     if (IS_MECH(mech, MECH_NAME_PLAIN) == 0) {
78         cbsasl_mechs_t plain_mech = get_plain_mechs();
79         memcpy(&(*conn)->c.server.mech, &plain_mech, sizeof(cbsasl_mechs_t));
80     } else if (IS_MECH(mech, MECH_NAME_CRAM_MD5) == 0) {
81         cbsasl_mechs_t cram_md5_mech = get_cram_md5_mechs();
82         memcpy(&(*conn)->c.server.mech, &cram_md5_mech, sizeof(cbsasl_mechs_t));
83     } else {
84         cbsasl_dispose(conn);
85         return SASL_BADPARAM;
86     }
87 
88     if ((err = (*conn)->c.server.mech.init()) != SASL_OK) {
89         cbsasl_dispose(conn);
90         return err;
91     }
92 
93     err = (*conn)->c.server.mech.start(*conn);
94     if (serverout) {
95         *serverout = (void*)(*conn)->c.server.sasl_data;
96     }
97     if (serveroutlen) {
98         *serveroutlen = (*conn)->c.server.sasl_data_len;
99     }
100 
101     if (err == SASL_CONTINUE && clientinlen != 0) {
102         return cbsasl_server_step(*conn, clientin, clientinlen,
103                                   (const char**)serverout, serveroutlen);
104     }
105 
106     return err;
107 }
108 
109 CBSASL_PUBLIC_API
cbsasl_server_step(cbsasl_conn_t *conn, const char *input, unsigned inputlen, const char **output, unsigned *outputlen)110 cbsasl_error_t cbsasl_server_step(cbsasl_conn_t *conn,
111                                   const char *input,
112                                   unsigned inputlen,
113                                   const char **output,
114                                   unsigned *outputlen)
115 {
116     if (conn == NULL || conn->client) {
117         return SASL_BADPARAM;
118     }
119     return conn->c.server.mech.step(conn, input, inputlen, output, outputlen);
120 }
121 
122 CBSASL_PUBLIC_API
cbsasl_server_refresh(void)123 cbsasl_error_t cbsasl_server_refresh(void)
124 {
125     return load_user_db();
126 }
127 
128 CBSASL_PUBLIC_API
cbsasl_getprop(cbsasl_conn_t *conn, cbsasl_prop_t propnum, const void **pvalue)129 cbsasl_error_t cbsasl_getprop(cbsasl_conn_t *conn,
130                               cbsasl_prop_t propnum,
131                               const void **pvalue)
132 {
133     if (conn->client || pvalue == NULL) {
134         return SASL_BADPARAM;
135     }
136 
137     switch (propnum) {
138     case CBSASL_USERNAME:
139         *pvalue = conn->c.server.username;
140         break;
141     case CBSASL_CONFIG:
142         *pvalue = conn->c.server.config;
143         break;
144     default:
145         return SASL_BADPARAM;
146     }
147 
148     return SASL_OK;
149 }
150 
151 CBSASL_PUBLIC_API
cbsasl_setprop(cbsasl_conn_t *conn, cbsasl_prop_t propnum, const void *pvalue)152 cbsasl_error_t cbsasl_setprop(cbsasl_conn_t *conn,
153                               cbsasl_prop_t propnum,
154                               const void *pvalue)
155 {
156     void *old;
157     if (conn->client) {
158         return SASL_BADPARAM;
159     }
160 
161     switch (propnum) {
162     case CBSASL_USERNAME:
163         old = conn->c.server.username;
164         if ((conn->c.server.username = strdup(pvalue)) == NULL) {
165             conn->c.server.username = old;
166             return SASL_NOMEM;
167         }
168         break;
169     case CBSASL_CONFIG:
170         old = conn->c.server.config;
171         if ((conn->c.server.config = strdup(pvalue)) == NULL) {
172             conn->c.server.config = old;
173             return SASL_NOMEM;
174         }
175         break;
176     default:
177         return SASL_BADPARAM;
178     }
179 
180     free(old);
181     return SASL_OK;
182 
183 }
184 
185 /* This function is added to keep the randgen static ;-) */
cbsasl_secure_random(char *dest, size_t len)186 cbsasl_error_t cbsasl_secure_random(char *dest, size_t len) {
187     return (cb_rand_get(randgen, dest, len) == 0) ? SASL_OK : SASL_FAIL;
188 }
189