1/* Plain SASL plugin
2 * Rob Siemborski
3 * Tim Martin
4 * $Id: plain.c,v 1.7 2006/02/03 22:33:14 snsimon Exp $
5 */
6/*
7 * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in
18 *    the documentation and/or other materials provided with the
19 *    distribution.
20 *
21 * 3. The name "Carnegie Mellon University" must not be used to
22 *    endorse or promote products derived from this software without
23 *    prior written permission. For permission or any other legal
24 *    details, please contact
25 *      Office of Technology Transfer
26 *      Carnegie Mellon University
27 *      5000 Forbes Avenue
28 *      Pittsburgh, PA  15213-3890
29 *      (412) 268-4387, fax: (412) 268-7395
30 *      tech-transfer@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//static const char plugin_id[] = "$Id: plain.c,v 1.7 2006/02/03 22:33:14 snsimon Exp $";
61
62/*****************************  Server Section  *****************************/
63
64static int plain_server_mech_new(void *glob_context __attribute__((unused)),
65				 sasl_server_params_t *sparams,
66				 const char *challenge __attribute__((unused)),
67				 unsigned challen __attribute__((unused)),
68				 void **conn_context)
69{
70    /* holds state are in */
71    if (!conn_context) {
72	PARAMERROR( sparams->utils );
73	return SASL_BADPARAM;
74    }
75
76    *conn_context = NULL;
77
78    return SASL_OK;
79}
80
81static int plain_server_mech_step(void *conn_context __attribute__((unused)),
82				  sasl_server_params_t *params,
83				  const char *clientin,
84				  unsigned clientinlen,
85				  const char **serverout,
86				  unsigned *serveroutlen,
87				  sasl_out_params_t *oparams)
88{
89    const char *author;
90    const char *authen;
91    const char *password;
92    size_t password_len;
93    unsigned lup=0;
94    int result;
95    char *passcopy;
96
97    *serverout = NULL;
98    *serveroutlen = 0;
99
100    /* should have received author-id NUL authen-id NUL password */
101
102    /* get author */
103    author = clientin;
104    while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
105
106    if (lup >= clientinlen) {
107	SETERROR(params->utils, "Can only find author (no password)");
108	return SASL_BADPROT;
109    }
110
111    /* get authen */
112    ++lup;
113    authen = clientin + lup;
114    while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
115
116    if (lup >= clientinlen) {
117	params->utils->seterror(params->utils->conn, 0,
118				"Can only find author/en (no password)");
119	return SASL_BADPROT;
120    }
121
122    /* get password */
123    lup++;
124    password = clientin + lup;
125    while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
126
127    password_len = clientin + lup - password;
128
129    if (lup != clientinlen) {
130	SETERROR(params->utils,
131		 "Got more data than we were expecting in the PLAIN plugin\n");
132	return SASL_BADPROT;
133    }
134
135    /* this kinda sucks. we need password to be null terminated
136       but we can't assume there is an allocated byte at the end
137       of password so we have to copy it */
138    passcopy = params->utils->malloc(password_len + 1);
139    if (passcopy == NULL) {
140	MEMERROR(params->utils);
141	return SASL_NOMEM;
142    }
143
144    strncpy(passcopy, password, password_len);
145    passcopy[password_len] = '\0';
146
147    /* Canonicalize userid first, so that password verification is only
148     * against the canonical id */
149    if (!author || !*author)
150	author = authen;
151
152    result = params->canon_user(params->utils->conn,
153				authen, 0, SASL_CU_AUTHID, oparams);
154    if (result != SASL_OK) {
155	_plug_free_string(params->utils, &passcopy);
156	return result;
157    }
158
159    /* verify password - return sasl_ok on success*/
160    result = params->utils->checkpass(params->utils->conn,
161				      oparams->authid, oparams->alen,
162				      passcopy, password_len);
163
164    _plug_free_string(params->utils, &passcopy);
165
166    if (result != SASL_OK) {
167	params->utils->seterror(params->utils->conn, 0,
168				"Password verification failed");
169	return result;
170    }
171
172    /* Canonicalize and store the authorization ID */
173    /* We need to do this after calling verify_user just in case verify_user
174     * needed to get auxprops itself */
175    result = params->canon_user(params->utils->conn,
176				author, 0, SASL_CU_AUTHZID, oparams);
177    if (result != SASL_OK) return result;
178
179    /* set oparams */
180    oparams->doneflag = 1;
181    oparams->mech_ssf = 0;
182    oparams->maxoutbuf = 0;
183    oparams->encode_context = NULL;
184    oparams->encode = NULL;
185    oparams->decode_context = NULL;
186    oparams->decode = NULL;
187    oparams->param_version = 0;
188
189    return SASL_OK;
190}
191
192static sasl_server_plug_t plain_server_plugins[] =
193{
194    {
195	"PLAIN",			/* mech_name */
196	0,				/* max_ssf */
197	SASL_SEC_NOANONYMOUS,		/* security_flags */
198	SASL_FEAT_WANT_CLIENT_FIRST
199	| SASL_FEAT_ALLOWS_PROXY,	/* features */
200	NULL,				/* glob_context */
201	&plain_server_mech_new,		/* mech_new */
202	&plain_server_mech_step,	/* mech_step */
203	NULL,				/* mech_dispose */
204	NULL,				/* mech_free */
205	NULL,				/* setpass */
206	NULL,				/* user_query */
207	NULL,				/* idle */
208	NULL,				/* mech_avail */
209	NULL				/* spare */
210    }
211};
212
213int plain_server_plug_init(const sasl_utils_t *utils,
214			   int maxversion,
215			   int *out_version,
216			   sasl_server_plug_t **pluglist,
217			   int *plugcount)
218{
219    if (maxversion < SASL_SERVER_PLUG_VERSION) {
220	SETERROR(utils, "PLAIN version mismatch");
221	return SASL_BADVERS;
222    }
223
224    *out_version = SASL_SERVER_PLUG_VERSION;
225    *pluglist = plain_server_plugins;
226    *plugcount = 1;
227
228    return SASL_OK;
229}
230
231/*****************************  Client Section  *****************************/
232
233typedef struct client_context {
234    char *out_buf;
235    unsigned out_buf_len;
236} client_context_t;
237
238static int plain_client_mech_new(void *glob_context __attribute__((unused)),
239				 sasl_client_params_t *params,
240				 void **conn_context)
241{
242    client_context_t *text;
243
244    /* holds state are in */
245    text = params->utils->malloc(sizeof(client_context_t));
246    if (text == NULL) {
247	MEMERROR( params->utils );
248	return SASL_NOMEM;
249    }
250
251    memset(text, 0, sizeof(client_context_t));
252
253    *conn_context = text;
254
255    return SASL_OK;
256}
257
258static int plain_client_mech_step(void *conn_context,
259				  sasl_client_params_t *params,
260				  const char *serverin __attribute__((unused)),
261				  unsigned serverinlen __attribute__((unused)),
262				  sasl_interact_t **prompt_need,
263				  const char **clientout,
264				  unsigned *clientoutlen,
265				  sasl_out_params_t *oparams)
266{
267    client_context_t *text = (client_context_t *) conn_context;
268    const char *user = NULL, *authid = NULL;
269    sasl_secret_t *password = NULL;
270    unsigned int free_password = 0; /* set if we need to free password */
271    int user_result = SASL_OK;
272    int auth_result = SASL_OK;
273    int pass_result = SASL_OK;
274    int result;
275    char *p;
276
277    *clientout = NULL;
278    *clientoutlen = 0;
279
280    /* doesn't really matter how the server responds */
281
282    /* check if sec layer strong enough */
283    if (params->props.min_ssf > params->external_ssf) {
284	SETERROR( params->utils, "SSF requested of PLAIN plugin");
285	return SASL_TOOWEAK;
286    }
287
288    /* try to get the authid */
289    if (oparams->authid == NULL) {
290	auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
291
292	if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
293	    return auth_result;
294    }
295
296    /* try to get the userid */
297    if (oparams->user == NULL) {
298	user_result = _plug_get_userid(params->utils, &user, prompt_need);
299
300	if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
301	    return user_result;
302    }
303
304    /* try to get the password */
305    if (password == NULL) {
306	pass_result = _plug_get_password(params->utils, &password,
307					 &free_password, prompt_need);
308
309	if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
310	    return pass_result;
311    }
312
313    /* free prompts we got */
314    if (prompt_need && *prompt_need) {
315	params->utils->free(*prompt_need);
316	*prompt_need = NULL;
317    }
318
319    /* if there are prompts not filled in */
320    if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
321	(pass_result == SASL_INTERACT)) {
322	/* make the prompt list */
323	result =
324	    _plug_make_prompts(params->utils, prompt_need,
325			       user_result == SASL_INTERACT ?
326			       "Please enter your authorization name" : NULL,
327			       NULL,
328			       auth_result == SASL_INTERACT ?
329			       "Please enter your authentication name" : NULL,
330			       NULL,
331			       pass_result == SASL_INTERACT ?
332			       "Please enter your password" : NULL, NULL,
333			       NULL, NULL, NULL,
334			       NULL, NULL, NULL);
335	if (result != SASL_OK) goto cleanup;
336
337	return SASL_INTERACT;
338    }
339
340    if (!password) {
341	PARAMERROR(params->utils);
342	return SASL_BADPARAM;
343    }
344
345    if (!user || !*user) {
346	result = params->canon_user(params->utils->conn, authid, 0,
347				    SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
348    }
349    else {
350	result = params->canon_user(params->utils->conn, user, 0,
351				    SASL_CU_AUTHZID, oparams);
352	if (result != SASL_OK) goto cleanup;
353
354	result = params->canon_user(params->utils->conn, authid, 0,
355				    SASL_CU_AUTHID, oparams);
356    }
357    if (result != SASL_OK) goto cleanup;
358
359    /* send authorized id NUL authentication id NUL password */
360    *clientoutlen = ((user && *user ? oparams->ulen : 0) +
361		     1 + oparams->alen +
362		     1 + password->len);
363
364    /* remember the extra NUL on the end for stupid clients */
365    result = _plug_buf_alloc(params->utils, (unsigned char **)&(text->out_buf),
366			     &(text->out_buf_len), *clientoutlen + 1);
367    if (result != SASL_OK) goto cleanup;
368
369    memset(text->out_buf, 0, *clientoutlen + 1);
370    p = text->out_buf;
371    if (user && *user) {
372	memcpy(p, oparams->user, oparams->ulen);
373	p += oparams->ulen;
374    }
375    memcpy(++p, oparams->authid, oparams->alen);
376    p += oparams->alen;
377    memcpy(++p, password->data, password->len);
378
379    *clientout = text->out_buf;
380
381    /* set oparams */
382    oparams->doneflag = 1;
383    oparams->mech_ssf = 0;
384    oparams->maxoutbuf = 0;
385    oparams->encode_context = NULL;
386    oparams->encode = NULL;
387    oparams->decode_context = NULL;
388    oparams->decode = NULL;
389    oparams->param_version = 0;
390
391    result = SASL_OK;
392
393  cleanup:
394    /* free sensitive info */
395    if (free_password) _plug_free_secret(params->utils, &password);
396
397    return result;
398}
399
400static void plain_client_mech_dispose(void *conn_context,
401				      const sasl_utils_t *utils)
402{
403    client_context_t *text = (client_context_t *) conn_context;
404
405    if (!text) return;
406
407    if (text->out_buf) utils->free(text->out_buf);
408
409    utils->free(text);
410}
411
412static sasl_client_plug_t plain_client_plugins[] =
413{
414    {
415	"PLAIN",			/* mech_name */
416	0,				/* max_ssf */
417	SASL_SEC_NOANONYMOUS,		/* security_flags */
418	SASL_FEAT_WANT_CLIENT_FIRST
419	| SASL_FEAT_ALLOWS_PROXY,	/* features */
420	NULL,				/* required_prompts */
421	NULL,				/* glob_context */
422	&plain_client_mech_new,		/* mech_new */
423	&plain_client_mech_step,	/* mech_step */
424	&plain_client_mech_dispose,	/* mech_dispose */
425	NULL,				/* mech_free */
426	NULL,				/* idle */
427	NULL,				/* spare */
428	NULL				/* spare */
429    }
430};
431
432int plain_client_plug_init(sasl_utils_t *utils,
433			   int maxversion,
434			   int *out_version,
435			   sasl_client_plug_t **pluglist,
436			   int *plugcount)
437{
438    if (maxversion < SASL_CLIENT_PLUG_VERSION) {
439	SETERROR(utils, "PLAIN version mismatch");
440	return SASL_BADVERS;
441    }
442
443    *out_version = SASL_CLIENT_PLUG_VERSION;
444    *pluglist = plain_client_plugins;
445    *plugcount = 1;
446
447    return SASL_OK;
448}
449