1 /* Plain SASL plugin
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 #include <stdio.h>
48 #include <string.h>
49 #include <sasl.h>
50 #include <saslplug.h>
51
52 #include "plugin_common.h"
53
54 #ifdef macintosh
55 #include <sasl_plain_plugin_decl.h>
56 #endif
57
58 /***************************** Common Section *****************************/
59
60 /***************************** Server Section *****************************/
61
plain_server_mech_new(void * glob_context,sasl_server_params_t * sparams,const char * challenge,unsigned challen,void ** conn_context)62 static int plain_server_mech_new(void *glob_context __attribute__((unused)),
63 sasl_server_params_t *sparams,
64 const char *challenge __attribute__((unused)),
65 unsigned challen __attribute__((unused)),
66 void **conn_context)
67 {
68 /* holds state are in */
69 if (!conn_context) {
70 PARAMERROR( sparams->utils );
71 return SASL_BADPARAM;
72 }
73
74 *conn_context = NULL;
75
76 return SASL_OK;
77 }
78
plain_server_mech_step(void * conn_context,sasl_server_params_t * params,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)79 static int plain_server_mech_step(void *conn_context __attribute__((unused)),
80 sasl_server_params_t *params,
81 const char *clientin,
82 unsigned clientinlen,
83 const char **serverout,
84 unsigned *serveroutlen,
85 sasl_out_params_t *oparams)
86 {
87 const char *author;
88 const char *authen;
89 const char *password;
90 unsigned password_len;
91 unsigned lup = 0;
92 int result;
93 char *passcopy;
94 unsigned canon_flags = 0;
95
96 *serverout = NULL;
97 *serveroutlen = 0;
98
99 /* should have received author-id NUL authen-id NUL password */
100
101 /* get author */
102 author = clientin;
103 while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
104
105 if (lup >= clientinlen) {
106 SETERROR(params->utils, "Can only find author (no password)");
107 return SASL_BADPROT;
108 }
109
110 /* get authen */
111 ++lup;
112 authen = clientin + lup;
113 while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
114
115 if (lup >= clientinlen) {
116 params->utils->seterror(params->utils->conn, 0,
117 "Can only find author/en (no password)");
118 return SASL_BADPROT;
119 }
120
121 /* get password */
122 lup++;
123 password = clientin + lup;
124 while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
125
126 password_len = (unsigned) (clientin + lup - password);
127
128 if (lup != clientinlen) {
129 SETERROR(params->utils,
130 "Got more data than we were expecting in the PLAIN plugin\n");
131 return SASL_BADPROT;
132 }
133
134 /* this kinda sucks. we need password to be null terminated
135 but we can't assume there is an allocated byte at the end
136 of password so we have to copy it */
137 passcopy = params->utils->malloc(password_len + 1);
138 if (passcopy == NULL) {
139 MEMERROR(params->utils);
140 return SASL_NOMEM;
141 }
142
143 strncpy(passcopy, password, password_len);
144 passcopy[password_len] = '\0';
145
146 /* Canonicalize userid first, so that password verification is only
147 * against the canonical id */
148 if (!author || !*author) {
149 author = authen;
150 canon_flags = SASL_CU_AUTHZID;
151 } else if (strcmp(author, authen) == 0) {
152 /* While this isn't going to find out that <user> and <user>@<defaultdomain>
153 are the same thing, this is good enough for many cases */
154 canon_flags = SASL_CU_AUTHZID;
155 }
156
157 result = params->canon_user(params->utils->conn,
158 authen,
159 0,
160 SASL_CU_AUTHID | canon_flags | SASL_CU_EXTERNALLY_VERIFIED,
161 oparams);
162 if (result != SASL_OK) {
163 _plug_free_string(params->utils, &passcopy);
164 return result;
165 }
166
167 /* verify password (and possibly fetch both authentication and
168 authorization identity related properties) - return SASL_OK
169 on success */
170 result = params->utils->checkpass(params->utils->conn,
171 oparams->authid,
172 oparams->alen,
173 passcopy,
174 password_len);
175
176 _plug_free_string(params->utils, &passcopy);
177
178 if (result != SASL_OK) {
179 params->utils->seterror(params->utils->conn, 0,
180 "Password verification failed");
181 return result;
182 }
183
184 /* Canonicalize and store the authorization ID */
185 /* We need to do this after calling verify_user just in case verify_user
186 * needed to get auxprops itself */
187 if (canon_flags == 0) {
188 const struct propval *pr;
189 int i;
190
191 pr = params->utils->prop_get(params->propctx);
192 if (!pr) {
193 return SASL_FAIL;
194 }
195
196 /* params->utils->checkpass() might have fetched authorization identity related properties
197 for the wrong user name. Free these values. */
198 for (i = 0; pr[i].name; i++) {
199 if (pr[i].name[0] == '*') {
200 continue;
201 }
202
203 if (pr[i].values) {
204 params->utils->prop_erase(params->propctx, pr[i].name);
205 }
206 }
207
208 result = params->canon_user(params->utils->conn,
209 author,
210 0,
211 SASL_CU_AUTHZID,
212 oparams);
213 if (result != SASL_OK) {
214 return result;
215 }
216 }
217
218 /* set oparams */
219 oparams->doneflag = 1;
220 oparams->mech_ssf = 0;
221 oparams->maxoutbuf = 0;
222 oparams->encode_context = NULL;
223 oparams->encode = NULL;
224 oparams->decode_context = NULL;
225 oparams->decode = NULL;
226 oparams->param_version = 0;
227
228 return SASL_OK;
229 }
230
231 static sasl_server_plug_t plain_server_plugins[] =
232 {
233 {
234 "PLAIN", /* mech_name */
235 0, /* max_ssf */
236 SASL_SEC_NOANONYMOUS
237 | SASL_SEC_PASS_CREDENTIALS, /* security_flags */
238 SASL_FEAT_WANT_CLIENT_FIRST
239 | SASL_FEAT_ALLOWS_PROXY, /* features */
240 NULL, /* glob_context */
241 &plain_server_mech_new, /* mech_new */
242 &plain_server_mech_step, /* mech_step */
243 NULL, /* mech_dispose */
244 NULL, /* mech_free */
245 NULL, /* setpass */
246 NULL, /* user_query */
247 NULL, /* idle */
248 NULL, /* mech_avail */
249 NULL /* spare */
250 }
251 };
252
plain_server_plug_init(const sasl_utils_t * utils,int maxversion,int * out_version,sasl_server_plug_t ** pluglist,int * plugcount)253 int plain_server_plug_init(const sasl_utils_t *utils,
254 int maxversion,
255 int *out_version,
256 sasl_server_plug_t **pluglist,
257 int *plugcount)
258 {
259 if (maxversion < SASL_SERVER_PLUG_VERSION) {
260 SETERROR(utils, "PLAIN version mismatch");
261 return SASL_BADVERS;
262 }
263
264 *out_version = SASL_SERVER_PLUG_VERSION;
265 *pluglist = plain_server_plugins;
266 *plugcount = 1;
267
268 return SASL_OK;
269 }
270
271 /***************************** Client Section *****************************/
272
273 typedef struct client_context {
274 char *out_buf;
275 unsigned out_buf_len;
276 } client_context_t;
277
plain_client_mech_new(void * glob_context,sasl_client_params_t * params,void ** conn_context)278 static int plain_client_mech_new(void *glob_context __attribute__((unused)),
279 sasl_client_params_t *params,
280 void **conn_context)
281 {
282 client_context_t *text;
283
284 /* holds state are in */
285 text = params->utils->malloc(sizeof(client_context_t));
286 if (text == NULL) {
287 MEMERROR( params->utils );
288 return SASL_NOMEM;
289 }
290
291 memset(text, 0, sizeof(client_context_t));
292
293 *conn_context = text;
294
295 return SASL_OK;
296 }
297
plain_client_mech_step(void * conn_context,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)298 static int plain_client_mech_step(void *conn_context,
299 sasl_client_params_t *params,
300 const char *serverin __attribute__((unused)),
301 unsigned serverinlen __attribute__((unused)),
302 sasl_interact_t **prompt_need,
303 const char **clientout,
304 unsigned *clientoutlen,
305 sasl_out_params_t *oparams)
306 {
307 client_context_t *text = (client_context_t *) conn_context;
308 const char *user = NULL, *authid = NULL;
309 sasl_secret_t *password = NULL;
310 unsigned int free_password = 0; /* set if we need to free password */
311 int user_result = SASL_OK;
312 int auth_result = SASL_OK;
313 int pass_result = SASL_OK;
314 int result;
315 char *p;
316
317 *clientout = NULL;
318 *clientoutlen = 0;
319
320 /* doesn't really matter how the server responds */
321
322 /* check if sec layer strong enough */
323 if (params->props.min_ssf > params->external_ssf) {
324 SETERROR( params->utils, "SSF requested of PLAIN plugin");
325 return SASL_TOOWEAK;
326 }
327
328 /* try to get the authid */
329 if (oparams->authid == NULL) {
330 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
331
332 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
333 return auth_result;
334 }
335
336 /* try to get the userid */
337 if (oparams->user == NULL) {
338 user_result = _plug_get_userid(params->utils, &user, prompt_need);
339
340 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
341 return user_result;
342 }
343
344 /* try to get the password */
345 if (password == NULL) {
346 pass_result = _plug_get_password(params->utils, &password,
347 &free_password, prompt_need);
348
349 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
350 return pass_result;
351 }
352
353 /* free prompts we got */
354 if (prompt_need && *prompt_need) {
355 params->utils->free(*prompt_need);
356 *prompt_need = NULL;
357 }
358
359 /* if there are prompts not filled in */
360 if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
361 (pass_result == SASL_INTERACT)) {
362 /* make the prompt list */
363 result =
364 _plug_make_prompts(params->utils, prompt_need,
365 user_result == SASL_INTERACT ?
366 "Please enter your authorization name" : NULL,
367 NULL,
368 auth_result == SASL_INTERACT ?
369 "Please enter your authentication name" : NULL,
370 NULL,
371 pass_result == SASL_INTERACT ?
372 "Please enter your password" : NULL, NULL,
373 NULL, NULL, NULL,
374 NULL, NULL, NULL);
375 if (result != SASL_OK) goto cleanup;
376
377 return SASL_INTERACT;
378 }
379
380 if (!password) {
381 PARAMERROR(params->utils);
382 return SASL_BADPARAM;
383 }
384
385 if (!user || !*user) {
386 result = params->canon_user(params->utils->conn, authid, 0,
387 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
388 }
389 else {
390 result = params->canon_user(params->utils->conn, user, 0,
391 SASL_CU_AUTHZID, oparams);
392 if (result != SASL_OK) goto cleanup;
393
394 result = params->canon_user(params->utils->conn, authid, 0,
395 SASL_CU_AUTHID, oparams);
396 }
397 if (result != SASL_OK) goto cleanup;
398
399 /* send authorized id NUL authentication id NUL password */
400 *clientoutlen = ((user && *user ? oparams->ulen : 0) +
401 1 + oparams->alen +
402 1 + password->len);
403
404 /* remember the extra NUL on the end for stupid clients */
405 result = _plug_buf_alloc(params->utils, &(text->out_buf),
406 &(text->out_buf_len), *clientoutlen + 1);
407 if (result != SASL_OK) goto cleanup;
408
409 memset(text->out_buf, 0, *clientoutlen + 1);
410 p = text->out_buf;
411 if (user && *user) {
412 memcpy(p, oparams->user, oparams->ulen);
413 p += oparams->ulen;
414 }
415 memcpy(++p, oparams->authid, oparams->alen);
416 p += oparams->alen;
417 memcpy(++p, password->data, password->len);
418
419 *clientout = text->out_buf;
420
421 /* set oparams */
422 oparams->doneflag = 1;
423 oparams->mech_ssf = 0;
424 oparams->maxoutbuf = 0;
425 oparams->encode_context = NULL;
426 oparams->encode = NULL;
427 oparams->decode_context = NULL;
428 oparams->decode = NULL;
429 oparams->param_version = 0;
430
431 result = SASL_OK;
432
433 cleanup:
434 /* free sensitive info */
435 if (free_password) _plug_free_secret(params->utils, &password);
436
437 return result;
438 }
439
plain_client_mech_dispose(void * conn_context,const sasl_utils_t * utils)440 static void plain_client_mech_dispose(void *conn_context,
441 const sasl_utils_t *utils)
442 {
443 client_context_t *text = (client_context_t *) conn_context;
444
445 if (!text) return;
446
447 if (text->out_buf) utils->free(text->out_buf);
448
449 utils->free(text);
450 }
451
452 static sasl_client_plug_t plain_client_plugins[] =
453 {
454 {
455 "PLAIN", /* mech_name */
456 0, /* max_ssf */
457 SASL_SEC_NOANONYMOUS
458 | SASL_SEC_PASS_CREDENTIALS, /* security_flags */
459 SASL_FEAT_WANT_CLIENT_FIRST
460 | SASL_FEAT_ALLOWS_PROXY, /* features */
461 NULL, /* required_prompts */
462 NULL, /* glob_context */
463 &plain_client_mech_new, /* mech_new */
464 &plain_client_mech_step, /* mech_step */
465 &plain_client_mech_dispose, /* mech_dispose */
466 NULL, /* mech_free */
467 NULL, /* idle */
468 NULL, /* spare */
469 NULL /* spare */
470 }
471 };
472
plain_client_plug_init(sasl_utils_t * utils,int maxversion,int * out_version,sasl_client_plug_t ** pluglist,int * plugcount)473 int plain_client_plug_init(sasl_utils_t *utils,
474 int maxversion,
475 int *out_version,
476 sasl_client_plug_t **pluglist,
477 int *plugcount)
478 {
479 if (maxversion < SASL_CLIENT_PLUG_VERSION) {
480 SETERROR(utils, "PLAIN version mismatch");
481 return SASL_BADVERS;
482 }
483
484 *out_version = SASL_CLIENT_PLUG_VERSION;
485 *pluglist = plain_client_plugins;
486 *plugcount = 1;
487
488 return SASL_OK;
489 }
490