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