1 /*
2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 #pragma ident	"%Z%%M%	%I%	%E% SMI"
6 
7 /* Plain SASL plugin
8  * Rob Siemborski
9  * Tim Martin
10  * $Id: plain.c,v 1.61 2003/03/26 17:18:04 rjs3 Exp $
11  */
12 
13 /*
14  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  *
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  *
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in
25  *    the documentation and/or other materials provided with the
26  *    distribution.
27  *
28  * 3. The name "Carnegie Mellon University" must not be used to
29  *    endorse or promote products derived from this software without
30  *    prior written permission. For permission or any other legal
31  *    details, please contact
32  *      Office of Technology Transfer
33  *      Carnegie Mellon University
34  *      5000 Forbes Avenue
35  *      Pittsburgh, PA  15213-3890
36  *      (412) 268-4387, fax: (412) 268-7395
37  *      tech-transfer@andrew.cmu.edu
38  *
39  * 4. Redistributions of any form whatsoever must retain the following
40  *    acknowledgment:
41  *    "This product includes software developed by Computing Services
42  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
43  *
44  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
45  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
46  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
47  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
48  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
49  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
50  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51  */
52 
53 #include <config.h>
54 #include <stdio.h>
55 #include <string.h>
56 #include <sasl.h>
57 #include <saslplug.h>
58 
59 #include "plugin_common.h"
60 
61 #ifndef _SUN_SDK_
62 #ifdef WIN32
63 /* This must be after sasl.h */
64 # include "saslPLAIN.h"
65 #endif /* WIN32 */
66 #endif /* !_SUN_SDK_ */
67 
68 #ifdef macintosh
69 #include <sasl_plain_plugin_decl.h>
70 #endif
71 
72 /*****************************  Common Section  *****************************/
73 
74 #ifndef _SUN_SDK_
75 static const char plugin_id[] = "$Id: plain.c,v 1.61 2003/03/26 17:18:04 rjs3 Exp $";
76 #endif /* !_SUN_SDK_ */
77 
78 /*****************************  Server Section  *****************************/
79 
80 static int plain_server_mech_new(void *glob_context __attribute__((unused)),
81 				 sasl_server_params_t *sparams,
82 				 const char *challenge __attribute__((unused)),
83 				 unsigned challen __attribute__((unused)),
84 				 void **conn_context)
85 {
86     /* holds state are in */
87     if (!conn_context) {
88 	PARAMERROR( sparams->utils );
89 	return SASL_BADPARAM;
90     }
91 
92     *conn_context = NULL;
93 
94     return SASL_OK;
95 }
96 
97 static int plain_server_mech_step(void *conn_context __attribute__((unused)),
98 				  sasl_server_params_t *params,
99 				  const char *clientin,
100 				  unsigned clientinlen,
101 				  const char **serverout,
102 				  unsigned *serveroutlen,
103 				  sasl_out_params_t *oparams)
104 {
105     const char *author;
106     const char *authen;
107     const char *password;
108     size_t password_len;
109     unsigned lup=0;
110     int result;
111     char *passcopy;
112 
113     *serverout = NULL;
114     *serveroutlen = 0;
115 
116     /* should have received author-id NUL authen-id NUL password */
117 
118     /* get author */
119     author = clientin;
120     while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
121 
122     if (lup >= clientinlen) {
123 #ifdef _SUN_SDK_
124 	params->utils->log(params->utils->conn, SASL_LOG_ERR,
125 		"Can only find author (no password)");
126 #else
127 	SETERROR(params->utils, "Can only find author (no password)");
128 #endif /* _SUN_SDK_ */
129 	return SASL_BADPROT;
130     }
131 
132     /* get authen */
133     ++lup;
134     authen = clientin + lup;
135     while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
136 
137     if (lup >= clientinlen) {
138 #ifdef _SUN_SDK_
139 	params->utils->log(params->utils->conn, SASL_LOG_ERR,
140 			"Can only find author/en (no password)");
141 #else
142 	params->utils->seterror(params->utils->conn, 0,
143 				"Can only find author/en (no password)");
144 #endif /* _SUN_SDK_ */
145 	return SASL_BADPROT;
146     }
147 
148     /* get password */
149     lup++;
150     password = clientin + lup;
151     while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
152 
153     password_len = clientin + lup - password;
154 
155     if (lup != clientinlen) {
156 #ifdef _SUN_SDK_
157 	params->utils->log(params->utils->conn, SASL_LOG_ERR,
158 		"Got more data than we were expecting in the PLAIN plugin");
159 #else
160 	SETERROR(params->utils,
161 		 "Got more data than we were expecting in the PLAIN plugin\n");
162 #endif /* _SUN_SDK_ */
163 	return SASL_BADPROT;
164     }
165 
166     /* this kinda sucks. we need password to be null terminated
167        but we can't assume there is an allocated byte at the end
168        of password so we have to copy it */
169     passcopy = params->utils->malloc(password_len + 1);
170     if (passcopy == NULL) {
171 	MEMERROR(params->utils);
172 	return SASL_NOMEM;
173     }
174 
175     strncpy(passcopy, password, password_len);
176     passcopy[password_len] = '\0';
177 
178     /* Canonicalize userid first, so that password verification is only
179      * against the canonical id */
180     if (!author || !*author)
181 	author = authen;
182 
183     result = params->canon_user(params->utils->conn,
184 				authen, 0, SASL_CU_AUTHID, oparams);
185     if (result != SASL_OK) {
186 	_plug_free_string(params->utils, &passcopy);
187 	return result;
188     }
189 
190     /* verify password - return sasl_ok on success*/
191     result = params->utils->checkpass(params->utils->conn,
192 				      oparams->authid, oparams->alen,
193 				      passcopy, password_len);
194 
195     _plug_free_string(params->utils, &passcopy);
196 
197     if (result != SASL_OK) {
198 #ifdef _INTEGRATED_SOLARIS_
199 	params->utils->seterror(params->utils->conn, 0,
200 				gettext("Password verification failed"));
201 #else
202 	params->utils->seterror(params->utils->conn, 0,
203 				"Password verification failed");
204 #endif /* _INTEGRATED_SOLARIS_ */
205 	return result;
206     }
207 
208     /* Canonicalize and store the authorization ID */
209     /* We need to do this after calling verify_user just in case verify_user
210      * needed to get auxprops itself */
211     result = params->canon_user(params->utils->conn,
212 				author, 0, SASL_CU_AUTHZID, oparams);
213     if (result != SASL_OK) return result;
214 
215     /* Transition? */
216     if (params->transition) {
217 	params->transition(params->utils->conn, password, password_len);
218     }
219 
220     /* set oparams */
221     oparams->doneflag = 1;
222     oparams->mech_ssf = 0;
223     oparams->maxoutbuf = 0;
224     oparams->encode_context = NULL;
225     oparams->encode = NULL;
226     oparams->decode_context = NULL;
227     oparams->decode = NULL;
228     oparams->param_version = 0;
229 
230     return SASL_OK;
231 }
232 
233 static sasl_server_plug_t plain_server_plugins[] =
234 {
235     {
236 	"PLAIN",			/* mech_name */
237 	0,				/* max_ssf */
238 	SASL_SEC_NOANONYMOUS,		/* security_flags */
239 	SASL_FEAT_WANT_CLIENT_FIRST
240 	| SASL_FEAT_ALLOWS_PROXY,	/* features */
241 	NULL,				/* glob_context */
242 	&plain_server_mech_new,		/* mech_new */
243 	&plain_server_mech_step,	/* mech_step */
244 	NULL,				/* mech_dispose */
245 	NULL,				/* mech_free */
246 	NULL,				/* setpass */
247 	NULL,				/* user_query */
248 	NULL,				/* idle */
249 	NULL,				/* mech_avail */
250 	NULL				/* spare */
251     }
252 };
253 
254 int plain_server_plug_init(const sasl_utils_t *utils,
255 			   int maxversion,
256 			   int *out_version,
257 			   sasl_server_plug_t **pluglist,
258 			   int *plugcount)
259 {
260     if (maxversion < SASL_SERVER_PLUG_VERSION) {
261 	SETERROR(utils, "PLAIN version mismatch");
262 	return SASL_BADVERS;
263     }
264 
265     *out_version = SASL_SERVER_PLUG_VERSION;
266     *pluglist = plain_server_plugins;
267     *plugcount = 1;
268 
269     return SASL_OK;
270 }
271 
272 /*****************************  Client Section  *****************************/
273 
274 typedef struct client_context {
275     char *out_buf;
276     unsigned out_buf_len;
277 #ifdef _INTEGRATED_SOLARIS_
278     void *h;
279 #endif /* _INTEGRATED_SOLARIS_ */
280 } client_context_t;
281 
282 static int plain_client_mech_new(void *glob_context __attribute__((unused)),
283 				 sasl_client_params_t *params,
284 				 void **conn_context)
285 {
286     client_context_t *text;
287 
288     /* holds state are in */
289     text = params->utils->malloc(sizeof(client_context_t));
290     if (text == NULL) {
291 	MEMERROR( params->utils );
292 	return SASL_NOMEM;
293     }
294 
295     memset(text, 0, sizeof(client_context_t));
296 
297     *conn_context = text;
298 
299     return SASL_OK;
300 }
301 
302 static int plain_client_mech_step(void *conn_context,
303 				  sasl_client_params_t *params,
304 				  const char *serverin __attribute__((unused)),
305 				  unsigned serverinlen __attribute__((unused)),
306 				  sasl_interact_t **prompt_need,
307 				  const char **clientout,
308 				  unsigned *clientoutlen,
309 				  sasl_out_params_t *oparams)
310 {
311     client_context_t *text = (client_context_t *) conn_context;
312     const char *user = NULL, *authid = NULL;
313     sasl_secret_t *password = NULL;
314     unsigned int free_password = 0; /* set if we need to free password */
315     int user_result = SASL_OK;
316     int auth_result = SASL_OK;
317     int pass_result = SASL_OK;
318     int result;
319 
320     *clientout = NULL;
321     *clientoutlen = 0;
322 
323     /* doesn't really matter how the server responds */
324 
325     /* check if sec layer strong enough */
326     if (params->props.min_ssf > params->external_ssf) {
327 #ifdef _INTEGRATED_SOLARIS_
328 	SETERROR( params->utils, gettext("SSF requested of PLAIN plugin"));
329 #else
330 	SETERROR( params->utils, "SSF requested of PLAIN plugin");
331 #endif /* _INTEGRATED_SOLARIS_ */
332 	return SASL_TOOWEAK;
333     }
334 
335     /* try to get the authid */
336     if (oparams->authid == NULL) {
337 	auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
338 
339 	if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
340 	    return auth_result;
341     }
342 
343     /* try to get the userid */
344     if (oparams->user == NULL) {
345 	user_result = _plug_get_userid(params->utils, &user, prompt_need);
346 
347 	if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
348 	    return user_result;
349     }
350 
351     /* try to get the password */
352     if (password == NULL) {
353 	pass_result = _plug_get_password(params->utils, &password,
354 					 &free_password, prompt_need);
355 
356 	if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
357 	    return pass_result;
358     }
359 
360     /* free prompts we got */
361     if (prompt_need && *prompt_need) {
362 	params->utils->free(*prompt_need);
363 	*prompt_need = NULL;
364     }
365 
366     /* if there are prompts not filled in */
367     if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
368 	(pass_result == SASL_INTERACT)) {
369 	/* make the prompt list */
370 	result =
371 #ifdef _INTEGRATED_SOLARIS_
372 	    _plug_make_prompts(params->utils, &text->h, prompt_need,
373 			       user_result == SASL_INTERACT ?
374 			       convert_prompt(params->utils, &text->h,
375 				gettext("Please enter your authorization name"))
376 					: NULL,
377 			       NULL,
378 			       auth_result == SASL_INTERACT ?
379 			       convert_prompt(params->utils, &text->h,
380 			gettext("Please enter your authentication name"))
381 					: NULL,
382 			       NULL,
383 			       pass_result == SASL_INTERACT ?
384 			       convert_prompt(params->utils, &text->h,
385 				gettext("Please enter your password")) : NULL,
386 				NULL,
387 			       NULL, NULL, NULL,
388 			       NULL, NULL, NULL);
389 #else
390 	    _plug_make_prompts(params->utils, prompt_need,
391 			       user_result == SASL_INTERACT ?
392 			       "Please enter your authorization name" : NULL,
393 			       NULL,
394 			       auth_result == SASL_INTERACT ?
395 			       "Please enter your authentication name" : NULL,
396 			       NULL,
397 			       pass_result == SASL_INTERACT ?
398 			       "Please enter your password" : NULL, NULL,
399 			       NULL, NULL, NULL,
400 			       NULL, NULL, NULL);
401 #endif /* _INTEGRATED_SOLARIS_ */
402 	if (result != SASL_OK) goto cleanup;
403 
404 	return SASL_INTERACT;
405     }
406 
407     if (!password) {
408 	PARAMERROR(params->utils);
409 	return SASL_BADPARAM;
410     }
411 
412     if (!user || !*user) {
413 	result = params->canon_user(params->utils->conn, authid, 0,
414 				    SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
415     }
416     else {
417 	result = params->canon_user(params->utils->conn, user, 0,
418 				    SASL_CU_AUTHZID, oparams);
419 	if (result != SASL_OK) goto cleanup;
420 
421 	result = params->canon_user(params->utils->conn, authid, 0,
422 				    SASL_CU_AUTHID, oparams);
423     }
424     if (result != SASL_OK) goto cleanup;
425 
426     /* send authorized id NUL authentication id NUL password */
427     *clientoutlen = (oparams->ulen + 1
428 		     + oparams->alen + 1
429 		     + password->len);
430 
431     /* remember the extra NUL on the end for stupid clients */
432     result = _plug_buf_alloc(params->utils, &(text->out_buf),
433 			     &(text->out_buf_len), *clientoutlen + 1);
434     if (result != SASL_OK) goto cleanup;
435 
436     memset(text->out_buf, 0, *clientoutlen + 1);
437     memcpy(text->out_buf, oparams->user, oparams->ulen);
438     memcpy(text->out_buf + oparams->ulen + 1, oparams->authid, oparams->alen);
439     memcpy(text->out_buf + oparams->ulen + oparams->alen + 2,
440 	   password->data, password->len);
441 
442     *clientout = text->out_buf;
443 
444     /* set oparams */
445     oparams->doneflag = 1;
446     oparams->mech_ssf = 0;
447     oparams->maxoutbuf = 0;
448     oparams->encode_context = NULL;
449     oparams->encode = NULL;
450     oparams->decode_context = NULL;
451     oparams->decode = NULL;
452     oparams->param_version = 0;
453 
454     result = SASL_OK;
455 
456   cleanup:
457     /* free sensitive info */
458     if (free_password) _plug_free_secret(params->utils, &password);
459 
460     return result;
461 }
462 
463 static void plain_client_mech_dispose(void *conn_context,
464 				      const sasl_utils_t *utils)
465 {
466     client_context_t *text = (client_context_t *) conn_context;
467 
468     if (!text) return;
469 
470     if (text->out_buf) utils->free(text->out_buf);
471 #ifdef _INTEGRATED_SOLARIS_
472     convert_prompt(utils, &text->h, NULL);
473 #endif /* _INTEGRATED_SOLARIS_ */
474 
475     utils->free(text);
476 }
477 
478 static sasl_client_plug_t plain_client_plugins[] =
479 {
480     {
481 	"PLAIN",			/* mech_name */
482 	0,				/* max_ssf */
483 	SASL_SEC_NOANONYMOUS,		/* security_flags */
484 	SASL_FEAT_WANT_CLIENT_FIRST
485 	| SASL_FEAT_ALLOWS_PROXY,	/* features */
486 	NULL,				/* required_prompts */
487 	NULL,				/* glob_context */
488 	&plain_client_mech_new,		/* mech_new */
489 	&plain_client_mech_step,	/* mech_step */
490 	&plain_client_mech_dispose,	/* mech_dispose */
491 	NULL,				/* mech_free */
492 	NULL,				/* idle */
493 	NULL,				/* spare */
494 	NULL				/* spare */
495     }
496 };
497 
498 int plain_client_plug_init(sasl_utils_t *utils,
499 			   int maxversion,
500 			   int *out_version,
501 			   sasl_client_plug_t **pluglist,
502 			   int *plugcount)
503 {
504     if (maxversion < SASL_CLIENT_PLUG_VERSION) {
505 	SETERROR(utils, "PLAIN version mismatch");
506 	return SASL_BADVERS;
507     }
508 
509     *out_version = SASL_CLIENT_PLUG_VERSION;
510     *pluglist = plain_client_plugins;
511     *plugcount = 1;
512 
513     return SASL_OK;
514 }
515