1 /* SASL server API implementation
2 * Rob Siemborski
3 * Tim Martin
4 */
5 /*
6 * Copyright (c) 1998-2016 Carnegie Mellon University. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The name "Carnegie Mellon University" must not be used to
21 * endorse or promote products derived from this software without
22 * prior written permission. For permission or any other legal
23 * details, please contact
24 * Carnegie Mellon University
25 * Center for Technology Transfer and Enterprise Creation
26 * 4615 Forbes Avenue
27 * Suite 302
28 * Pittsburgh, PA 15213
29 * (412) 268-7393, fax: (412) 268-7395
30 * innovation@andrew.cmu.edu
31 *
32 * 4. Redistributions of any form whatsoever must retain the following
33 * acknowledgment:
34 * "This product includes software developed by Computing Services
35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
36 *
37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
44 */
45
46 #include <config.h>
47
48 /* sasldb stuff */
49
50 #include <stdio.h>
51
52 #include "sasl.h"
53 #include "saslutil.h"
54 #include "saslplug.h"
55 #include "../sasldb/sasldb.h"
56
57 #include "plugin_common.h"
58
sasldb_auxprop_lookup(void * glob_context,sasl_server_params_t * sparams,unsigned flags,const char * user,unsigned ulen)59 static int sasldb_auxprop_lookup(void *glob_context __attribute__((unused)),
60 sasl_server_params_t *sparams,
61 unsigned flags,
62 const char *user,
63 unsigned ulen)
64 {
65 char *userid = NULL;
66 char *realm = NULL;
67 const char *user_realm = NULL;
68 int ret;
69 const struct propval *to_fetch, *cur;
70 char value[8192];
71 size_t value_len;
72 char *user_buf;
73 int verify_against_hashed_password;
74 int saw_user_password = 0;
75
76 if (!sparams || !user) return SASL_BADPARAM;
77
78 user_buf = sparams->utils->malloc(ulen + 1);
79 if(!user_buf) {
80 ret = SASL_NOMEM;
81 goto done;
82 }
83
84 memcpy(user_buf, user, ulen);
85 user_buf[ulen] = '\0';
86
87 if(sparams->user_realm) {
88 user_realm = sparams->user_realm;
89 } else {
90 user_realm = sparams->serverFQDN;
91 }
92
93 ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm,
94 sparams->serverFQDN, user_buf);
95 if(ret != SASL_OK) goto done;
96
97 to_fetch = sparams->utils->prop_get(sparams->propctx);
98 if (!to_fetch) {
99 ret = SASL_NOMEM;
100 goto done;
101 }
102
103 verify_against_hashed_password = flags & SASL_AUXPROP_VERIFY_AGAINST_HASH;
104
105 /* Use a fake value to signal that we have no property to lookup */
106 ret = SASL_CONTINUE;
107 for(cur = to_fetch; cur->name; cur++) {
108 int cur_ret;
109 const char *realname = cur->name;
110
111 /* Only look up properties that apply to this lookup! */
112 if(cur->name[0] == '*' && (flags & SASL_AUXPROP_AUTHZID)) continue;
113 if(!(flags & SASL_AUXPROP_AUTHZID)) {
114 if(cur->name[0] != '*') continue;
115 else realname = cur->name + 1;
116 }
117
118 /* If it's there already, we want to see if it needs to be
119 * overridden. userPassword is a special case, because it's value
120 is always present if SASL_AUXPROP_VERIFY_AGAINST_HASH is specified.
121 When SASL_AUXPROP_VERIFY_AGAINST_HASH is set, we just clear userPassword. */
122 if (cur->values && !(flags & SASL_AUXPROP_OVERRIDE) &&
123 (verify_against_hashed_password == 0 ||
124 strcasecmp(realname, SASL_AUX_PASSWORD_PROP) != 0)) {
125 continue;
126 } else if (cur->values) {
127 sparams->utils->prop_erase(sparams->propctx, cur->name);
128 }
129
130 if (strcasecmp(realname, SASL_AUX_PASSWORD_PROP) == 0) {
131 saw_user_password = 1;
132 }
133
134 cur_ret = _sasldb_getdata(sparams->utils,
135 sparams->utils->conn, userid, realm,
136 realname, value, sizeof(value), &value_len);
137
138 /* Assumption: cur_ret is never SASL_CONTINUE */
139
140 /* If this is the first property we've tried to fetch ==>
141 always set the global error code.
142 If we had SASL_NOUSER ==> any other error code overrides it
143 (including SASL_NOUSER). */
144 if (ret == SASL_CONTINUE || ret == SASL_NOUSER) {
145 ret = cur_ret;
146 } else if (ret == SASL_OK) {
147 /* Any error code other than SASL_NOUSER overrides SASL_OK.
148 (And SASL_OK overrides SASL_OK as well) */
149 if (cur_ret != SASL_NOUSER) {
150 ret = cur_ret;
151 }
152 }
153 /* Any other global error code is left as is */
154
155 if (cur_ret != SASL_OK) {
156 if (cur_ret != SASL_NOUSER) {
157 /* No point in continuing if we hit any serious error */
158 break;
159 }
160 /* We didn't find it, leave it as not found */
161 continue;
162 }
163
164 sparams->utils->prop_set(sparams->propctx, cur->name,
165 value, (unsigned) value_len);
166 }
167
168 /* [Keep in sync with LDAPDB, SQL]
169 If ret is SASL_CONTINUE, it means that no properties were requested
170 (or maybe some were requested, but they already have values and
171 SASL_AUXPROP_OVERRIDE flag is not set).
172 Always return SASL_OK in this case. */
173 if (ret == SASL_CONTINUE) {
174 ret = SASL_OK;
175 }
176
177 if (flags & SASL_AUXPROP_AUTHZID) {
178 /* This is a lie, but the caller can't handle
179 when we return SASL_NOUSER for authorization identity lookup. */
180 if (ret == SASL_NOUSER) {
181 ret = SASL_OK;
182 }
183 } else {
184 if (ret == SASL_NOUSER && saw_user_password == 0) {
185 /* Verify user existence by checking presence of
186 the userPassword attribute */
187 ret = _sasldb_getdata(sparams->utils,
188 sparams->utils->conn,
189 userid,
190 realm,
191 SASL_AUX_PASSWORD_PROP,
192 value,
193 sizeof(value),
194 &value_len);
195 }
196 }
197
198 done:
199 if (userid) sparams->utils->free(userid);
200 if (realm) sparams->utils->free(realm);
201 if (user_buf) sparams->utils->free(user_buf);
202
203 return ret;
204 }
205
sasldb_auxprop_store(void * glob_context,sasl_server_params_t * sparams,struct propctx * ctx,const char * user,unsigned ulen)206 static int sasldb_auxprop_store(void *glob_context __attribute__((unused)),
207 sasl_server_params_t *sparams,
208 struct propctx *ctx,
209 const char *user,
210 unsigned ulen)
211 {
212 char *userid = NULL;
213 char *realm = NULL;
214 const char *user_realm = NULL;
215 int ret = SASL_FAIL;
216 const struct propval *to_store, *cur;
217 char *user_buf;
218
219 /* just checking if we are enabled */
220 if(!ctx) return SASL_OK;
221
222 if(!sparams || !user) return SASL_BADPARAM;
223
224 user_buf = sparams->utils->malloc(ulen + 1);
225 if(!user_buf) {
226 ret = SASL_NOMEM;
227 goto done;
228 }
229
230 memcpy(user_buf, user, ulen);
231 user_buf[ulen] = '\0';
232
233 if(sparams->user_realm) {
234 user_realm = sparams->user_realm;
235 } else {
236 user_realm = sparams->serverFQDN;
237 }
238
239 ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm,
240 sparams->serverFQDN, user_buf);
241 if(ret != SASL_OK) goto done;
242
243 to_store = sparams->utils->prop_get(ctx);
244 if(!to_store) {
245 ret = SASL_BADPARAM;
246 goto done;
247 }
248
249 ret = SASL_OK;
250 for (cur = to_store; cur->name; cur++) {
251 const char *value = (cur->values && cur->values[0]) ? cur->values[0] : NULL;
252
253 if (cur->name[0] == '*') {
254 continue;
255 }
256
257 /* WARN: We only support one value right now. */
258 ret = _sasldb_putdata(sparams->utils,
259 sparams->utils->conn,
260 userid,
261 realm,
262 cur->name,
263 value,
264 value ? strlen(value) : 0);
265
266 if (value == NULL && ret == SASL_NOUSER) {
267 /* Deleting something which is not there is not an error */
268 ret = SASL_OK;
269 }
270
271 if (ret != SASL_OK) {
272 /* We've already failed, no point in continuing */
273 break;
274 }
275 }
276
277 done:
278 if (userid) sparams->utils->free(userid);
279 if (realm) sparams->utils->free(realm);
280 if (user_buf) sparams->utils->free(user_buf);
281
282 return ret;
283 }
284
285 static sasl_auxprop_plug_t sasldb_auxprop_plugin = {
286 0, /* Features */
287 0, /* spare */
288 NULL, /* glob_context */
289 sasldb_auxprop_free, /* auxprop_free */
290 sasldb_auxprop_lookup, /* auxprop_lookup */
291 "sasldb", /* name */
292 sasldb_auxprop_store /* auxprop_store */
293 };
294
sasldb_auxprop_plug_init(const sasl_utils_t * utils,int max_version,int * out_version,sasl_auxprop_plug_t ** plug,const char * plugname)295 int sasldb_auxprop_plug_init(const sasl_utils_t *utils,
296 int max_version,
297 int *out_version,
298 sasl_auxprop_plug_t **plug,
299 const char *plugname __attribute__((unused)))
300 {
301 if(!out_version || !plug) return SASL_BADPARAM;
302
303 /* Do we have database support? */
304 /* Note that we can use a NULL sasl_conn_t because our
305 * sasl_utils_t is "blessed" with the global callbacks */
306 if(_sasl_check_db(utils, NULL) != SASL_OK)
307 return SASL_NOMECH;
308
309 /* Check if libsasl API is older than ours. If it is, fail */
310 if(max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS;
311
312 *out_version = SASL_AUXPROP_PLUG_VERSION;
313
314 *plug = &sasldb_auxprop_plugin;
315
316 return SASL_OK;
317 }
318