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