1/*
2 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5#pragma ident	"%Z%%M%	%I%	%E% SMI"
6
7/* SASL server API implementation
8 * Rob Siemborski
9 * Tim Martin
10 * $Id: external.c,v 1.19 2003/04/08 17:30:54 rjs3 Exp $
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 <stdlib.h>
55#include <limits.h>
56#include <ctype.h>
57#include <string.h>
58#include <sasl.h>
59#include <saslplug.h>
60#include "saslint.h"
61
62#include <plugin_common.h>
63
64/*****************************  Common Section  *****************************/
65
66#ifndef _SUN_SDK_
67static const char plugin_id[] = "$Id: external.c,v 1.19 2003/04/08 17:30:54 rjs3 Exp $";
68#endif /* !_SUN_SDK_ */
69
70/*****************************  Server Section  *****************************/
71
72static int
73external_server_mech_new(void *glob_context __attribute__((unused)),
74			 sasl_server_params_t *sparams,
75			 const char *challenge __attribute__((unused)),
76			 unsigned challen __attribute__((unused)),
77			 void **conn_context)
78{
79    if (!conn_context
80	|| !sparams
81	|| !sparams->utils
82	|| !sparams->utils->conn)
83	return SASL_BADPARAM;
84
85    if (!sparams->utils->conn->external.auth_id)
86	return SASL_NOMECH;
87
88    *conn_context = NULL;
89
90    return SASL_OK;
91}
92
93static int
94external_server_mech_step(void *conn_context __attribute__((unused)),
95			  sasl_server_params_t *sparams,
96			  const char *clientin,
97			  unsigned clientinlen,
98			  const char **serverout,
99			  unsigned *serveroutlen,
100			  sasl_out_params_t *oparams)
101{
102    int result;
103
104    if (!sparams
105	|| !sparams->utils
106	|| !sparams->utils->conn
107	|| !sparams->utils->getcallback
108	|| !serverout
109	|| !serveroutlen
110	|| !oparams)
111	return SASL_BADPARAM;
112
113    if (!sparams->utils->conn->external.auth_id)
114	return SASL_BADPROT;
115
116    if ((sparams->props.security_flags & SASL_SEC_NOANONYMOUS) &&
117	(!strcmp(sparams->utils->conn->external.auth_id, "anonymous"))) {
118#ifdef _INTEGRATED_SOLARIS_
119	sasl_seterror(sparams->utils->conn,0,
120		gettext("anonymous login not allowed"));
121#else
122	sasl_seterror(sparams->utils->conn,0,"anonymous login not allowed");
123#endif /* _INTEGRATED_SOLARIS_ */
124	return SASL_NOAUTHZ;
125    }
126
127    *serverout = NULL;
128    *serveroutlen = 0;
129
130    if (!clientin) {
131	/* No initial data; we're in a protocol which doesn't support it.
132	 * So we let the server app know that we need some... */
133	return SASL_CONTINUE;
134    }
135
136    if (clientinlen) {		/* if we have a non-zero authorization id */
137	/* The user's trying to authorize as someone they didn't
138	 * authenticate as */
139	result = sparams->canon_user(sparams->utils->conn,
140				     clientin, 0, SASL_CU_AUTHZID, oparams);
141	if(result != SASL_OK) return result;
142
143	result = sparams->canon_user(sparams->utils->conn,
144				     sparams->utils->conn->external.auth_id, 0,
145				     SASL_CU_AUTHID, oparams);
146    } else {
147	result = sparams->canon_user(sparams->utils->conn,
148				     sparams->utils->conn->external.auth_id, 0,
149				     SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
150    }
151
152    if (result != SASL_OK) return result;
153
154    /* set oparams */
155    oparams->doneflag = 1;
156    oparams->mech_ssf = 0;
157    oparams->maxoutbuf = 0;
158    oparams->encode_context = NULL;
159    oparams->encode = NULL;
160    oparams->decode_context = NULL;
161    oparams->decode = NULL;
162    oparams->param_version = 0;
163
164    return SASL_OK;
165}
166
167static int
168external_server_mech_avail(void *glob_context __attribute__((unused)),
169			   sasl_server_params_t *sparams,
170			   void **conn_context __attribute__((unused)))
171{
172    if (!sparams->utils->conn->external.auth_id)
173	return SASL_NOMECH;
174    return SASL_OK;
175}
176
177static sasl_server_plug_t external_server_plugins[] =
178{
179    {
180	"EXTERNAL",			/* mech_name */
181	0,				/* max_ssf */
182	SASL_SEC_NOPLAINTEXT
183	| SASL_SEC_NOANONYMOUS
184	| SASL_SEC_NODICTIONARY,	/* security_flags */
185	SASL_FEAT_WANT_CLIENT_FIRST
186	| SASL_FEAT_ALLOWS_PROXY,	/* features */
187	NULL,				/* glob_context */
188	&external_server_mech_new,	/* mech_new */
189	&external_server_mech_step,	/* mech_step */
190	NULL,				/* mech_dispose */
191	NULL,				/* mech_free */
192	NULL,				/* setpass */
193	NULL,				/* user_query */
194	NULL,				/* idle */
195	&external_server_mech_avail,	/* mech_avail */
196	NULL				/* spare */
197    }
198};
199
200int external_server_plug_init(const sasl_utils_t *utils,
201			      int max_version,
202			      int *out_version,
203			      sasl_server_plug_t **pluglist,
204			      int *plugcount)
205{
206    if (!out_version || !pluglist || !plugcount)
207	return SASL_BADPARAM;
208
209    if (max_version != SASL_SERVER_PLUG_VERSION) {
210#ifdef _SUN_SDK_
211	utils->log(utils->conn, SASL_LOG_ERR, "EXTERNAL version mismatch");
212#else
213	SETERROR( utils, "EXTERNAL version mismatch" );
214#endif /* _SUN_SDK_ */
215	return SASL_BADVERS;
216    }
217
218    *out_version = SASL_SERVER_PLUG_VERSION;
219    *pluglist = external_server_plugins;
220    *plugcount = 1;
221    return SASL_OK;
222}
223
224/*****************************  Client Section  *****************************/
225
226typedef struct client_context
227{
228    char *out_buf;
229#ifdef _SUN_SDK_
230    unsigned out_buf_len;
231#else
232    size_t out_buf_len;
233#endif /* _SUN_SDK_ */
234#ifdef _INTEGRATED_SOLARIS_
235    void *h;
236#endif /* _INTEGRATED_SOLARIS_ */
237} client_context_t;
238
239static int external_client_mech_new(void *glob_context __attribute__((unused)),
240				    sasl_client_params_t *params,
241				    void **conn_context)
242{
243    client_context_t *text;
244
245    if (!params
246	|| !params->utils
247	|| !params->utils->conn
248	|| !conn_context)
249	return SASL_BADPARAM;
250
251    if (!params->utils->conn->external.auth_id)
252	return SASL_NOMECH;
253
254#ifdef _SUN_SDK_
255    text = params->utils->malloc(sizeof(client_context_t));
256#else
257    text = sasl_ALLOC(sizeof(client_context_t));
258#endif /* _SUN_SDK_ */
259    if(!text) return SASL_NOMEM;
260
261    memset(text, 0, sizeof(client_context_t));
262
263    *conn_context = text;
264
265    return SASL_OK;
266}
267
268static int
269external_client_mech_step(void *conn_context,
270			  sasl_client_params_t *params,
271			  const char *serverin __attribute__((unused)),
272			  unsigned serverinlen,
273			  sasl_interact_t **prompt_need,
274			  const char **clientout,
275			  unsigned *clientoutlen,
276			  sasl_out_params_t *oparams)
277{
278    client_context_t *text = (client_context_t *)conn_context;
279    const char *user = NULL;
280    int user_result = SASL_OK;
281    int result;
282
283    if (!params
284	|| !params->utils
285	|| !params->utils->conn
286	|| !params->utils->getcallback
287	|| !clientout
288	|| !clientoutlen
289	|| !oparams)
290	return SASL_BADPARAM;
291
292    if (!params->utils->conn->external.auth_id)
293	return SASL_BADPROT;
294
295    if (serverinlen != 0)
296	return SASL_BADPROT;
297
298    *clientout = NULL;
299    *clientoutlen = 0;
300
301    /* try to get the userid */
302    if (user == NULL) {
303	user_result = _plug_get_userid(params->utils, &user, prompt_need);
304
305	if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
306	    return user_result;
307    }
308
309    /* free prompts we got */
310    if (prompt_need && *prompt_need) {
311	params->utils->free(*prompt_need);
312	*prompt_need = NULL;
313    }
314
315    /* if there are prompts not filled in */
316    if (user_result == SASL_INTERACT) {
317	/* make the prompt list */
318	int result =
319#ifdef _INTEGRATED_SOLARIS_
320	    _plug_make_prompts(params->utils, &text->h, prompt_need,
321			       user_result == SASL_INTERACT ?
322			       convert_prompt(params->utils, &text->h,
323			       gettext("Please enter your authorization name"))
324				 : NULL,
325#else
326	    _plug_make_prompts(params->utils, prompt_need,
327			       user_result == SASL_INTERACT ?
328			       "Please enter your authorization name" : NULL,
329#endif /* _INTEGRATED_SOLARIS_ */
330			       "",
331			       NULL, NULL,
332			       NULL, NULL,
333			       NULL, NULL, NULL,
334			       NULL, NULL, NULL);
335	if (result != SASL_OK) return result;
336
337	return SASL_INTERACT;
338    }
339
340    *clientoutlen = user ? strlen(user) : 0;
341
342#ifdef _SUN_SDK_
343    result = _plug_buf_alloc(params->utils, &text->out_buf,
344        &text->out_buf_len, *clientoutlen + 1);
345#else
346    result = _buf_alloc(&text->out_buf, &text->out_buf_len, *clientoutlen + 1);
347#endif /* _SUN_SDK_ */
348
349    if (result != SASL_OK) return result;
350
351    if (user && *user) {
352	result = params->canon_user(params->utils->conn,
353				    user, 0, SASL_CU_AUTHZID, oparams);
354	if (result != SASL_OK) return result;
355
356	result = params->canon_user(params->utils->conn,
357				    params->utils->conn->external.auth_id, 0,
358				    SASL_CU_AUTHID, oparams);
359	if (result != SASL_OK) return result;
360
361	memcpy(text->out_buf, user, *clientoutlen);
362    } else {
363	result = params->canon_user(params->utils->conn,
364				    params->utils->conn->external.auth_id, 0,
365				    SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
366	if (result != SASL_OK) return result;
367    }
368
369    text->out_buf[*clientoutlen] = '\0';
370
371    *clientout = text->out_buf;
372
373    /* set oparams */
374    oparams->doneflag = 1;
375    oparams->mech_ssf = 0;
376    oparams->maxoutbuf = 0;
377    oparams->encode_context = NULL;
378    oparams->encode = NULL;
379    oparams->decode_context = NULL;
380    oparams->decode = NULL;
381    oparams->param_version = 0;
382
383    return SASL_OK;
384}
385
386static void
387external_client_mech_dispose(void *conn_context,
388			     const sasl_utils_t *utils __attribute__((unused)))
389{
390    client_context_t *text = (client_context_t *) conn_context;
391
392    if (!text) return;
393
394#ifdef _INTEGRATED_SOLARIS_
395    convert_prompt(utils, &text->h, NULL);
396#endif /* _INTEGRATED_SOLARIS_ */
397
398#ifdef _SUN_SDK_
399    if(text->out_buf) utils->free(text->out_buf);
400
401    utils->free(text);
402#else
403    if(text->out_buf) sasl_FREE(text->out_buf);
404
405    sasl_FREE(text);
406#endif /* _SUN_SDK_ */
407}
408
409#ifdef _SUN_SDK_
410static const unsigned long external_required_prompts[] = {
411#else
412static const long external_required_prompts[] = {
413#endif /* _SUN_SDK_ */
414    SASL_CB_LIST_END
415};
416
417static sasl_client_plug_t external_client_plugins[] =
418{
419    {
420	"EXTERNAL",			/* mech_name */
421	0,				/* max_ssf */
422	SASL_SEC_NOPLAINTEXT
423	| SASL_SEC_NOANONYMOUS
424	| SASL_SEC_NODICTIONARY,	/* security_flags */
425	SASL_FEAT_WANT_CLIENT_FIRST
426	| SASL_FEAT_ALLOWS_PROXY,	/* features */
427	external_required_prompts,	/* required_prompts */
428	NULL,				/* glob_context */
429	&external_client_mech_new,	/* mech_new */
430	&external_client_mech_step,	/* mech_step */
431	&external_client_mech_dispose,	/* mech_dispose */
432	NULL,				/* mech_free */
433	NULL,				/* idle */
434	NULL,				/* spare */
435	NULL				/* spare */
436    }
437};
438
439int external_client_plug_init(const sasl_utils_t *utils,
440			      int max_version,
441			      int *out_version,
442			      sasl_client_plug_t **pluglist,
443			      int *plugcount)
444{
445    if (!utils || !out_version || !pluglist || !plugcount)
446	return SASL_BADPARAM;
447
448    if (max_version != SASL_CLIENT_PLUG_VERSION) {
449#ifdef _SUN_SDK_
450	utils->log(utils->conn, SASL_LOG_ERR, "EXTERNAL version mismatch");
451#else
452	SETERROR( utils, "EXTERNAL version mismatch" );
453#endif /* _SUN_SDK_ */
454	return SASL_BADVERS;
455    }
456
457    *out_version = SASL_CLIENT_PLUG_VERSION;
458    *pluglist = external_client_plugins;
459    *plugcount = 1;
460
461    return SASL_OK;
462}
463