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_
75static 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
80static 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
97static 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
233static 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
254int 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
274typedef 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
282static 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
302static 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
463static 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
478static 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
498int 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