1 /* SASL server API implementation
2  * Rob Siemborski
3  * Tim Martin
4  */
5 /*
6  * Copyright (c) 1998-2016 Carnegie Mellon University.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The name "Carnegie Mellon University" must not be used to
21  *    endorse or promote products derived from this software without
22  *    prior written permission. For permission or any other legal
23  *    details, please contact
24  *      Carnegie Mellon University
25  *      Center for Technology Transfer and Enterprise Creation
26  *      4615 Forbes Avenue
27  *      Suite 302
28  *      Pittsburgh, PA  15213
29  *      (412) 268-7393, fax: (412) 268-7395
30  *      innovation@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 <stdlib.h>
49 #include <limits.h>
50 #include <ctype.h>
51 #include <string.h>
52 #include <sasl.h>
53 #include <saslplug.h>
54 #include "saslint.h"
55 
56 #include "../common/plugin_common.h"
57 
58 /*****************************  Common Section  *****************************/
59 
60 /*****************************  Server Section  *****************************/
61 
62 static int
external_server_mech_new(void * glob_context,sasl_server_params_t * sparams,const char * challenge,unsigned challen,void ** conn_context)63 external_server_mech_new(void *glob_context __attribute__((unused)),
64 			 sasl_server_params_t *sparams,
65 			 const char *challenge __attribute__((unused)),
66 			 unsigned challen __attribute__((unused)),
67 			 void **conn_context)
68 {
69     if (!conn_context
70 	|| !sparams
71 	|| !sparams->utils
72 	|| !sparams->utils->conn)
73 	return SASL_BADPARAM;
74 
75     if (!sparams->utils->conn->external.auth_id)
76 	return SASL_NOMECH;
77 
78     *conn_context = NULL;
79 
80     return SASL_OK;
81 }
82 
83 static int
external_server_mech_step(void * conn_context,sasl_server_params_t * sparams,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)84 external_server_mech_step(void *conn_context __attribute__((unused)),
85 			  sasl_server_params_t *sparams,
86 			  const char *clientin,
87 			  unsigned clientinlen,
88 			  const char **serverout,
89 			  unsigned *serveroutlen,
90 			  sasl_out_params_t *oparams)
91 {
92     int result;
93 
94     if (!sparams
95 	|| !sparams->utils
96 	|| !sparams->utils->conn
97 	|| !sparams->utils->getcallback
98 	|| !serverout
99 	|| !serveroutlen
100 	|| !oparams)
101 	return SASL_BADPARAM;
102 
103     if (!sparams->utils->conn->external.auth_id)
104 	return SASL_BADPROT;
105 
106     /* xxx arbitrary limit here */
107     if (clientinlen > 16384) return SASL_BADPROT;
108 
109     if ((sparams->props.security_flags & SASL_SEC_NOANONYMOUS) &&
110 	(!strcmp(sparams->utils->conn->external.auth_id, "anonymous"))) {
111 	sasl_seterror(sparams->utils->conn,0,"anonymous login not allowed");
112 	return SASL_NOAUTHZ;
113     }
114 
115     *serverout = NULL;
116     *serveroutlen = 0;
117 
118     if (!clientin) {
119 	/* No initial data; we're in a protocol which doesn't support it.
120 	 * So we let the server app know that we need some... */
121 	return SASL_CONTINUE;
122     }
123 
124     if (clientinlen) {		/* if we have a non-zero authorization id */
125 	/* The user's trying to authorize as someone they didn't
126 	 * authenticate as */
127 	result = sparams->canon_user(sparams->utils->conn,
128 				     clientin, clientinlen,
129 				     SASL_CU_AUTHZID, oparams);
130 	if(result != SASL_OK) return result;
131 
132 	result = sparams->canon_user(sparams->utils->conn,
133 				     sparams->utils->conn->external.auth_id, 0,
134 				     SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VERIFIED, oparams);
135     } else {
136 	result = sparams->canon_user(sparams->utils->conn,
137 				     sparams->utils->conn->external.auth_id, 0,
138 				     SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VERIFIED | SASL_CU_AUTHZID, oparams);
139     }
140 
141     if (result != SASL_OK) return result;
142 
143     /* set oparams */
144     oparams->doneflag = 1;
145     oparams->mech_ssf = 0;
146     oparams->maxoutbuf = 0;
147     oparams->encode_context = NULL;
148     oparams->encode = NULL;
149     oparams->decode_context = NULL;
150     oparams->decode = NULL;
151     oparams->param_version = 0;
152 
153     return SASL_OK;
154 }
155 
156 static int
external_server_mech_avail(void * glob_context,sasl_server_params_t * sparams,void ** conn_context)157 external_server_mech_avail(void *glob_context __attribute__((unused)),
158 			   sasl_server_params_t *sparams,
159 			   void **conn_context __attribute__((unused)))
160 {
161     if (!sparams->utils->conn->external.auth_id) {
162 	/* Return Temporary Failure */
163 	return SASL_NOTDONE;
164     }
165 
166     return SASL_OK;
167 }
168 
169 static sasl_server_plug_t external_server_plugins[] =
170 {
171     {
172 	"EXTERNAL",			/* mech_name */
173 	0,				/* max_ssf */
174 	SASL_SEC_NOPLAINTEXT
175 	| SASL_SEC_NOANONYMOUS
176 	| SASL_SEC_NODICTIONARY,	/* security_flags */
177 	SASL_FEAT_WANT_CLIENT_FIRST
178 	| SASL_FEAT_ALLOWS_PROXY,	/* features */
179 	NULL,				/* glob_context */
180 	&external_server_mech_new,	/* mech_new */
181 	&external_server_mech_step,	/* mech_step */
182 	NULL,				/* mech_dispose */
183 	NULL,				/* mech_free */
184 	NULL,				/* setpass */
185 	NULL,				/* user_query */
186 	NULL,				/* idle */
187 	&external_server_mech_avail,	/* mech_avail */
188 	NULL				/* spare */
189     }
190 };
191 
external_server_plug_init(const sasl_utils_t * utils,int max_version,int * out_version,sasl_server_plug_t ** pluglist,int * plugcount)192 int external_server_plug_init(const sasl_utils_t *utils,
193 			      int max_version,
194 			      int *out_version,
195 			      sasl_server_plug_t **pluglist,
196 			      int *plugcount)
197 {
198     if (!out_version || !pluglist || !plugcount)
199 	return SASL_BADPARAM;
200 
201     if (max_version != SASL_SERVER_PLUG_VERSION) {
202 	SETERROR( utils, "EXTERNAL version mismatch" );
203 	return SASL_BADVERS;
204     }
205 
206     *out_version = SASL_SERVER_PLUG_VERSION;
207     *pluglist = external_server_plugins;
208     *plugcount = 1;
209     return SASL_OK;
210 }
211 
212 /*****************************  Client Section  *****************************/
213 
214 typedef struct client_context
215 {
216     char *out_buf;
217     size_t out_buf_len;
218 } client_context_t;
219 
external_client_mech_new(void * glob_context,sasl_client_params_t * params,void ** conn_context)220 static int external_client_mech_new(void *glob_context __attribute__((unused)),
221 				    sasl_client_params_t *params,
222 				    void **conn_context)
223 {
224     client_context_t *text;
225 
226     if (!params
227 	|| !params->utils
228 	|| !params->utils->conn
229 	|| !conn_context)
230 	return SASL_BADPARAM;
231 
232     if (!params->utils->conn->external.auth_id)
233 	return SASL_NOMECH;
234 
235     text = sasl_ALLOC(sizeof(client_context_t));
236     if(!text) return SASL_NOMEM;
237 
238     memset(text, 0, sizeof(client_context_t));
239 
240     *conn_context = text;
241 
242     return SASL_OK;
243 }
244 
245 static int
external_client_mech_step(void * conn_context,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)246 external_client_mech_step(void *conn_context,
247 			  sasl_client_params_t *params,
248 			  const char *serverin __attribute__((unused)),
249 			  unsigned serverinlen,
250 			  sasl_interact_t **prompt_need,
251 			  const char **clientout,
252 			  unsigned *clientoutlen,
253 			  sasl_out_params_t *oparams)
254 {
255     client_context_t *text = (client_context_t *)conn_context;
256     const char *user = NULL;
257     int user_result = SASL_OK;
258     int result;
259 
260     if (!params
261 	|| !params->utils
262 	|| !params->utils->conn
263 	|| !params->utils->getcallback
264 	|| !clientout
265 	|| !clientoutlen
266 	|| !oparams)
267 	return SASL_BADPARAM;
268 
269     if (!params->utils->conn->external.auth_id)
270 	return SASL_BADPROT;
271 
272     if (serverinlen != 0)
273 	return SASL_BADPROT;
274 
275     *clientout = NULL;
276     *clientoutlen = 0;
277 
278     /* try to get the userid */
279     if (user == NULL) {
280 	user_result = _plug_get_userid(params->utils, &user, prompt_need);
281 
282 	if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
283 	    return user_result;
284     }
285 
286     /* free prompts we got */
287     if (prompt_need && *prompt_need) {
288 	params->utils->free(*prompt_need);
289 	*prompt_need = NULL;
290     }
291 
292     /* if there are prompts not filled in */
293     if (user_result == SASL_INTERACT) {
294 	/* make the prompt list */
295 	int result =
296 	    _plug_make_prompts(params->utils, prompt_need,
297 			       "Please enter your authorization name",
298 			       "",
299 			       NULL, NULL,
300 			       NULL, NULL,
301 			       NULL, NULL, NULL,
302 			       NULL, NULL, NULL);
303 	if (result != SASL_OK) return result;
304 
305 	return SASL_INTERACT;
306     }
307 
308     *clientoutlen = user ? (unsigned) strlen(user) : 0;
309 
310     result = _buf_alloc(&text->out_buf, &text->out_buf_len, *clientoutlen + 1);
311 
312     if (result != SASL_OK) return result;
313 
314     if (user && *user) {
315 	result = params->canon_user(params->utils->conn,
316 				    user, 0, SASL_CU_AUTHZID, oparams);
317 	if (result != SASL_OK) return result;
318 
319 	result = params->canon_user(params->utils->conn,
320 				    params->utils->conn->external.auth_id, 0,
321 				    SASL_CU_AUTHID, oparams);
322 	if (result != SASL_OK) return result;
323 
324 	memcpy(text->out_buf, user, *clientoutlen);
325     } else {
326 	result = params->canon_user(params->utils->conn,
327 				    params->utils->conn->external.auth_id, 0,
328 				    SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
329 	if (result != SASL_OK) return result;
330     }
331 
332     text->out_buf[*clientoutlen] = '\0';
333 
334     *clientout = text->out_buf;
335 
336     /* set oparams */
337     oparams->doneflag = 1;
338     oparams->mech_ssf = 0;
339     oparams->maxoutbuf = 0;
340     oparams->encode_context = NULL;
341     oparams->encode = NULL;
342     oparams->decode_context = NULL;
343     oparams->decode = NULL;
344     oparams->param_version = 0;
345 
346     return SASL_OK;
347 }
348 
349 static void
external_client_mech_dispose(void * conn_context,const sasl_utils_t * utils)350 external_client_mech_dispose(void *conn_context,
351 			     const sasl_utils_t *utils __attribute__((unused)))
352 {
353     client_context_t *text = (client_context_t *) conn_context;
354 
355     if (!text) return;
356 
357     if(text->out_buf) sasl_FREE(text->out_buf);
358 
359     sasl_FREE(text);
360 }
361 
362 static const unsigned long external_required_prompts[] = {
363     SASL_CB_LIST_END
364 };
365 
366 static sasl_client_plug_t external_client_plugins[] =
367 {
368     {
369 	"EXTERNAL",			/* mech_name */
370 	0,				/* max_ssf */
371 	SASL_SEC_NOPLAINTEXT
372 	| SASL_SEC_NOANONYMOUS
373 	| SASL_SEC_NODICTIONARY,	/* security_flags */
374 	SASL_FEAT_WANT_CLIENT_FIRST
375 	| SASL_FEAT_ALLOWS_PROXY,	/* features */
376 	external_required_prompts,	/* required_prompts */
377 	NULL,				/* glob_context */
378 	&external_client_mech_new,	/* mech_new */
379 	&external_client_mech_step,	/* mech_step */
380 	&external_client_mech_dispose,	/* mech_dispose */
381 	NULL,				/* mech_free */
382 	NULL,				/* idle */
383 	NULL,				/* spare */
384 	NULL				/* spare */
385     }
386 };
387 
external_client_plug_init(const sasl_utils_t * utils,int max_version,int * out_version,sasl_client_plug_t ** pluglist,int * plugcount)388 int external_client_plug_init(const sasl_utils_t *utils,
389 			      int max_version,
390 			      int *out_version,
391 			      sasl_client_plug_t **pluglist,
392 			      int *plugcount)
393 {
394     if (!utils || !out_version || !pluglist || !plugcount)
395 	return SASL_BADPARAM;
396 
397     if (max_version != SASL_CLIENT_PLUG_VERSION) {
398 	SETERROR( utils, "EXTERNAL version mismatch" );
399 	return SASL_BADVERS;
400     }
401 
402     *out_version = SASL_CLIENT_PLUG_VERSION;
403     *pluglist = external_client_plugins;
404     *plugcount = 1;
405 
406     return SASL_OK;
407 }
408