1/* Anonymous SASL plugin
2 * Rob Siemborski
3 * Tim Martin
4 * $Id: anonymous.c,v 1.8 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#ifdef HAVE_UNISTD_H
50#include <unistd.h>
51#endif
52#include <sasl.h>
53#include <saslplug.h>
54
55#include "plugin_common.h"
56
57#ifdef macintosh
58#include <sasl_anonymous_plugin_decl.h>
59#endif
60
61/*****************************  Common Section  *****************************/
62
63//static const char plugin_id[] = "$Id: anonymous.c,v 1.8 2006/02/03 22:33:14 snsimon Exp $";
64
65static const char anonymous_id[] = "anonymous";
66
67/*****************************  Server Section  *****************************/
68
69static int
70anonymous_server_mech_new(void *glob_context __attribute__((unused)),
71			  sasl_server_params_t *sparams,
72			  const char *challenge __attribute__((unused)),
73			  unsigned challen __attribute__((unused)),
74			  void **conn_context)
75{
76    /* holds state are in */
77    if (!conn_context) {
78	PARAMERROR( sparams->utils );
79	return SASL_BADPARAM;
80    }
81
82    *conn_context = NULL;
83
84    return SASL_OK;
85}
86
87static int
88anonymous_server_mech_step(void *conn_context __attribute__((unused)),
89			   sasl_server_params_t *sparams,
90			   const char *clientin,
91			   unsigned clientinlen,
92			   const char **serverout,
93			   unsigned *serveroutlen,
94			   sasl_out_params_t *oparams)
95{
96    char *clientdata;
97    int result;
98
99    if (!sparams
100	|| !serverout
101	|| !serveroutlen
102	|| !oparams) {
103	PARAMERROR( sparams->utils );
104	return SASL_BADPARAM;
105    }
106
107    *serverout = NULL;
108    *serveroutlen = 0;
109
110    if (!clientin) {
111	return SASL_CONTINUE;
112    }
113
114    /* We force a truncation 255 characters (specified by RFC 2245) */
115    if (clientinlen > 255) clientinlen = 255;
116
117    /* NULL-terminate the clientin... */
118    clientdata = sparams->utils->malloc(clientinlen + 1);
119    if (!clientdata) {
120	MEMERROR(sparams->utils);
121	return SASL_NOMEM;
122    }
123
124    strncpy(clientdata, clientin, clientinlen);
125    clientdata[clientinlen] = '\0';
126
127    sparams->utils->log(sparams->utils->conn,
128			SASL_LOG_NOTE,
129			"ANONYMOUS login: \"%s\"",
130			clientdata);
131
132    if (clientdata != clientin)
133	sparams->utils->free(clientdata);
134
135    result = sparams->canon_user(sparams->utils->conn,
136				 anonymous_id, 0,
137				 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
138
139    if (result != SASL_OK) return result;
140
141    /* set oparams */
142    oparams->doneflag = 1;
143    oparams->mech_ssf = 0;
144    oparams->maxoutbuf = 0;
145    oparams->encode_context = NULL;
146    oparams->encode = NULL;
147    oparams->decode_context = NULL;
148    oparams->decode = NULL;
149    oparams->param_version = 0;
150
151    return SASL_OK;
152}
153
154static sasl_server_plug_t anonymous_server_plugins[] =
155{
156    {
157	"ANONYMOUS",			/* mech_name */
158	0,				/* max_ssf */
159	SASL_SEC_NOPLAINTEXT,		/* security_flags */
160	SASL_FEAT_WANT_CLIENT_FIRST
161	| SASL_FEAT_DONTUSE_USERPASSWD,	/* features */
162	NULL,				/* glob_context */
163	&anonymous_server_mech_new,	/* mech_new */
164	&anonymous_server_mech_step,	/* mech_step */
165	NULL,				/* mech_dispose */
166	NULL,				/* mech_free */
167	NULL,				/* setpass */
168	NULL,				/* user_query */
169	NULL,				/* idle */
170	NULL,				/* mech_avail */
171	NULL                        	/* spare */
172    }
173};
174
175int anonymous_server_plug_init(const sasl_utils_t *utils,
176			       int maxversion,
177			       int *out_version,
178			       sasl_server_plug_t **pluglist,
179			       int *plugcount)
180{
181    if (maxversion < SASL_SERVER_PLUG_VERSION) {
182	SETERROR( utils, "ANONYMOUS version mismatch" );
183	return SASL_BADVERS;
184    }
185
186    *out_version = SASL_SERVER_PLUG_VERSION;
187    *pluglist = anonymous_server_plugins;
188    *plugcount = 1;
189
190    return SASL_OK;
191}
192
193/*****************************  Client Section  *****************************/
194
195typedef struct client_context {
196    char *out_buf;
197    unsigned out_buf_len;
198} client_context_t;
199
200static int
201anonymous_client_mech_new(void *glob_context __attribute__((unused)),
202			  sasl_client_params_t *cparams,
203			  void **conn_context)
204{
205    client_context_t *text;
206
207    if (!conn_context) {
208	PARAMERROR(cparams->utils);
209	return SASL_BADPARAM;
210    }
211
212    /* holds state are in */
213    text = cparams->utils->malloc(sizeof(client_context_t));
214    if (text == NULL) {
215	MEMERROR(cparams->utils);
216	return SASL_NOMEM;
217    }
218
219    memset(text, 0, sizeof(client_context_t));
220
221    *conn_context = text;
222
223    return SASL_OK;
224}
225
226static int
227anonymous_client_mech_step(void *conn_context,
228			   sasl_client_params_t *cparams,
229			   const char *serverin __attribute__((unused)),
230			   unsigned serverinlen,
231			   sasl_interact_t **prompt_need,
232			   const char **clientout,
233			   unsigned *clientoutlen,
234			   sasl_out_params_t *oparams)
235{
236    client_context_t *text = (client_context_t *) conn_context;
237    size_t userlen;
238    char hostname[256];
239    const char *user = NULL;
240    int user_result = SASL_OK;
241    int result;
242
243    if (!cparams
244	|| !clientout
245	|| !clientoutlen
246	|| !oparams) {
247	PARAMERROR( cparams->utils );
248	return SASL_BADPARAM;
249    }
250
251    *clientout = NULL;
252    *clientoutlen = 0;
253
254    if (serverinlen != 0) {
255	SETERROR( cparams->utils,
256		  "Nonzero serverinlen in ANONYMOUS continue_step" );
257	return SASL_BADPROT;
258    }
259
260    /* check if sec layer strong enough */
261    if (cparams->props.min_ssf > cparams->external_ssf) {
262	SETERROR( cparams->utils, "SSF requested of ANONYMOUS plugin");
263	return SASL_TOOWEAK;
264    }
265
266    /* try to get the trace info */
267    if (user == NULL) {
268	user_result = _plug_get_userid(cparams->utils, &user, prompt_need);
269
270	if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) {
271	    return user_result;
272	}
273    }
274
275    /* free prompts we got */
276    if (prompt_need && *prompt_need) {
277	cparams->utils->free(*prompt_need);
278	*prompt_need = NULL;
279    }
280
281    /* if there are prompts not filled in */
282    if (user_result == SASL_INTERACT) {
283	/* make the prompt list */
284	result =
285	    _plug_make_prompts(cparams->utils, prompt_need,
286			       user_result == SASL_INTERACT ?
287			       "Please enter anonymous identification" : NULL,
288			       "",
289			       NULL, NULL,
290			       NULL, NULL,
291			       NULL, NULL, NULL,
292			       NULL, NULL, NULL);
293	if (result != SASL_OK) return result;
294
295	return SASL_INTERACT;
296    }
297
298    if (!user || !*user) {
299	user = anonymous_id;
300    }
301    userlen = strlen(user);
302
303    result = cparams->canon_user(cparams->utils->conn,
304				 anonymous_id, 0,
305				 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
306    if (result != SASL_OK) return result;
307
308    memset(hostname, 0, sizeof(hostname));
309    gethostname(hostname, sizeof(hostname));
310    hostname[sizeof(hostname)-1] = '\0';
311
312    *clientoutlen = (unsigned) (userlen + strlen(hostname) + 1);
313
314    result = _plug_buf_alloc(cparams->utils, &text->out_buf,
315			     &text->out_buf_len, *clientoutlen);
316
317    if (result != SASL_OK) return result;
318
319    strcpy(text->out_buf, user);
320    text->out_buf[userlen] = '@';
321    /* use memcpy() instead of strcpy() so we don't add the NUL */
322    memcpy(text->out_buf + userlen + 1, hostname, strlen(hostname));
323
324    *clientout = text->out_buf;
325
326    /* set oparams */
327    oparams->doneflag = 1;
328    oparams->mech_ssf = 0;
329    oparams->maxoutbuf = 0;
330    oparams->encode_context = NULL;
331    oparams->encode = NULL;
332    oparams->decode_context = NULL;
333    oparams->decode = NULL;
334    oparams->param_version = 0;
335
336    return SASL_OK;
337}
338
339static void anonymous_client_dispose(void *conn_context,
340				     const sasl_utils_t *utils)
341{
342    client_context_t *text = (client_context_t *) conn_context;
343
344    if(!text) return;
345
346    if (text->out_buf) utils->free(text->out_buf);
347
348    utils->free(text);
349}
350
351static const unsigned long anonymous_required_prompts[] = {
352    SASL_CB_LIST_END
353};
354
355static sasl_client_plug_t anonymous_client_plugins[] =
356{
357    {
358	"ANONYMOUS",			/* mech_name */
359	0,				/* max_ssf */
360	SASL_SEC_NOPLAINTEXT,		/* security_flags */
361	SASL_FEAT_WANT_CLIENT_FIRST,	/* features */
362	anonymous_required_prompts,	/* required_prompts */
363	NULL,				/* glob_context */
364	&anonymous_client_mech_new, 	/* mech_new */
365	&anonymous_client_mech_step,	/* mech_step */
366	&anonymous_client_dispose,	/* mech_dispose */
367	NULL,				/* mech_free */
368	NULL,				/* idle */
369	NULL,				/* spare */
370	NULL                        	/* spare */
371    }
372};
373
374int anonymous_client_plug_init(const sasl_utils_t *utils,
375			       int maxversion,
376			       int *out_version,
377			       sasl_client_plug_t **pluglist,
378			       int *plugcount)
379{
380    if (maxversion < SASL_CLIENT_PLUG_VERSION) {
381	SETERROR( utils, "ANONYMOUS version mismatch" );
382	return SASL_BADVERS;
383    }
384
385    *out_version = SASL_CLIENT_PLUG_VERSION;
386    *pluglist = anonymous_client_plugins;
387    *plugcount = 1;
388
389    return SASL_OK;
390}
391