1 /*
2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /* Plain SASL plugin
7 * Rob Siemborski
8 * Tim Martin
9 * $Id: plain.c,v 1.61 2003/03/26 17:18:04 rjs3 Exp $
10 */
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 <string.h>
55 #include <sasl.h>
56 #include <saslplug.h>
57
58 #include "plugin_common.h"
59
60 #ifndef _SUN_SDK_
61 #ifdef WIN32
62 /* This must be after sasl.h */
63 # include "saslPLAIN.h"
64 #endif /* WIN32 */
65 #endif /* !_SUN_SDK_ */
66
67 #ifdef macintosh
68 #include <sasl_plain_plugin_decl.h>
69 #endif
70
71 /***************************** Common Section *****************************/
72
73 #ifndef _SUN_SDK_
74 static const char plugin_id[] = "$Id: plain.c,v 1.61 2003/03/26 17:18:04 rjs3 Exp $";
75 #endif /* !_SUN_SDK_ */
76
77 /***************************** Server Section *****************************/
78
plain_server_mech_new(void * glob_context,sasl_server_params_t * sparams,const char * challenge,unsigned challen,void ** conn_context)79 static int plain_server_mech_new(void *glob_context __attribute__((unused)),
80 sasl_server_params_t *sparams,
81 const char *challenge __attribute__((unused)),
82 unsigned challen __attribute__((unused)),
83 void **conn_context)
84 {
85 /* holds state are in */
86 if (!conn_context) {
87 PARAMERROR( sparams->utils );
88 return SASL_BADPARAM;
89 }
90
91 *conn_context = NULL;
92
93 return SASL_OK;
94 }
95
plain_server_mech_step(void * conn_context,sasl_server_params_t * params,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)96 static int plain_server_mech_step(void *conn_context __attribute__((unused)),
97 sasl_server_params_t *params,
98 const char *clientin,
99 unsigned clientinlen,
100 const char **serverout,
101 unsigned *serveroutlen,
102 sasl_out_params_t *oparams)
103 {
104 const char *author;
105 const char *authen;
106 const char *password;
107 size_t password_len;
108 unsigned lup=0;
109 int result;
110 char *passcopy;
111
112 *serverout = NULL;
113 *serveroutlen = 0;
114
115 /* should have received author-id NUL authen-id NUL password */
116
117 /* get author */
118 author = clientin;
119 while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
120
121 if (lup >= clientinlen) {
122 #ifdef _SUN_SDK_
123 params->utils->log(params->utils->conn, SASL_LOG_ERR,
124 "Can only find author (no password)");
125 #else
126 SETERROR(params->utils, "Can only find author (no password)");
127 #endif /* _SUN_SDK_ */
128 return SASL_BADPROT;
129 }
130
131 /* get authen */
132 ++lup;
133 authen = clientin + lup;
134 while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
135
136 if (lup >= clientinlen) {
137 #ifdef _SUN_SDK_
138 params->utils->log(params->utils->conn, SASL_LOG_ERR,
139 "Can only find author/en (no password)");
140 #else
141 params->utils->seterror(params->utils->conn, 0,
142 "Can only find author/en (no password)");
143 #endif /* _SUN_SDK_ */
144 return SASL_BADPROT;
145 }
146
147 /* get password */
148 lup++;
149 password = clientin + lup;
150 while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
151
152 password_len = clientin + lup - password;
153
154 if (lup != clientinlen) {
155 #ifdef _SUN_SDK_
156 params->utils->log(params->utils->conn, SASL_LOG_ERR,
157 "Got more data than we were expecting in the PLAIN plugin");
158 #else
159 SETERROR(params->utils,
160 "Got more data than we were expecting in the PLAIN plugin\n");
161 #endif /* _SUN_SDK_ */
162 return SASL_BADPROT;
163 }
164
165 /* this kinda sucks. we need password to be null terminated
166 but we can't assume there is an allocated byte at the end
167 of password so we have to copy it */
168 passcopy = params->utils->malloc(password_len + 1);
169 if (passcopy == NULL) {
170 MEMERROR(params->utils);
171 return SASL_NOMEM;
172 }
173
174 strncpy(passcopy, password, password_len);
175 passcopy[password_len] = '\0';
176
177 /* Canonicalize userid first, so that password verification is only
178 * against the canonical id */
179 if (!author || !*author)
180 author = authen;
181
182 result = params->canon_user(params->utils->conn,
183 authen, 0, SASL_CU_AUTHID, oparams);
184 if (result != SASL_OK) {
185 _plug_free_string(params->utils, &passcopy);
186 return result;
187 }
188
189 /* verify password - return sasl_ok on success*/
190 result = params->utils->checkpass(params->utils->conn,
191 oparams->authid, oparams->alen,
192 passcopy, password_len);
193
194 _plug_free_string(params->utils, &passcopy);
195
196 if (result != SASL_OK) {
197 #ifdef _INTEGRATED_SOLARIS_
198 params->utils->seterror(params->utils->conn, 0,
199 gettext("Password verification failed"));
200 #else
201 params->utils->seterror(params->utils->conn, 0,
202 "Password verification failed");
203 #endif /* _INTEGRATED_SOLARIS_ */
204 return result;
205 }
206
207 /* Canonicalize and store the authorization ID */
208 /* We need to do this after calling verify_user just in case verify_user
209 * needed to get auxprops itself */
210 result = params->canon_user(params->utils->conn,
211 author, 0, SASL_CU_AUTHZID, oparams);
212 if (result != SASL_OK) return result;
213
214 /* Transition? */
215 if (params->transition) {
216 params->transition(params->utils->conn, password, password_len);
217 }
218
219 /* set oparams */
220 oparams->doneflag = 1;
221 oparams->mech_ssf = 0;
222 oparams->maxoutbuf = 0;
223 oparams->encode_context = NULL;
224 oparams->encode = NULL;
225 oparams->decode_context = NULL;
226 oparams->decode = NULL;
227 oparams->param_version = 0;
228
229 return SASL_OK;
230 }
231
232 static sasl_server_plug_t plain_server_plugins[] =
233 {
234 {
235 "PLAIN", /* mech_name */
236 0, /* max_ssf */
237 SASL_SEC_NOANONYMOUS, /* security_flags */
238 SASL_FEAT_WANT_CLIENT_FIRST
239 | SASL_FEAT_ALLOWS_PROXY, /* features */
240 NULL, /* glob_context */
241 &plain_server_mech_new, /* mech_new */
242 &plain_server_mech_step, /* mech_step */
243 NULL, /* mech_dispose */
244 NULL, /* mech_free */
245 NULL, /* setpass */
246 NULL, /* user_query */
247 NULL, /* idle */
248 NULL, /* mech_avail */
249 NULL /* spare */
250 }
251 };
252
plain_server_plug_init(const sasl_utils_t * utils,int maxversion,int * out_version,sasl_server_plug_t ** pluglist,int * plugcount)253 int plain_server_plug_init(const sasl_utils_t *utils,
254 int maxversion,
255 int *out_version,
256 sasl_server_plug_t **pluglist,
257 int *plugcount)
258 {
259 if (maxversion < SASL_SERVER_PLUG_VERSION) {
260 SETERROR(utils, "PLAIN version mismatch");
261 return SASL_BADVERS;
262 }
263
264 *out_version = SASL_SERVER_PLUG_VERSION;
265 *pluglist = plain_server_plugins;
266 *plugcount = 1;
267
268 return SASL_OK;
269 }
270
271 /***************************** Client Section *****************************/
272
273 typedef struct client_context {
274 char *out_buf;
275 unsigned out_buf_len;
276 #ifdef _INTEGRATED_SOLARIS_
277 void *h;
278 #endif /* _INTEGRATED_SOLARIS_ */
279 } client_context_t;
280
plain_client_mech_new(void * glob_context,sasl_client_params_t * params,void ** conn_context)281 static int plain_client_mech_new(void *glob_context __attribute__((unused)),
282 sasl_client_params_t *params,
283 void **conn_context)
284 {
285 client_context_t *text;
286
287 /* holds state are in */
288 text = params->utils->malloc(sizeof(client_context_t));
289 if (text == NULL) {
290 MEMERROR( params->utils );
291 return SASL_NOMEM;
292 }
293
294 memset(text, 0, sizeof(client_context_t));
295
296 *conn_context = text;
297
298 return SASL_OK;
299 }
300
plain_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)301 static int plain_client_mech_step(void *conn_context,
302 sasl_client_params_t *params,
303 const char *serverin __attribute__((unused)),
304 unsigned serverinlen __attribute__((unused)),
305 sasl_interact_t **prompt_need,
306 const char **clientout,
307 unsigned *clientoutlen,
308 sasl_out_params_t *oparams)
309 {
310 client_context_t *text = (client_context_t *) conn_context;
311 const char *user = NULL, *authid = NULL;
312 sasl_secret_t *password = NULL;
313 unsigned int free_password = 0; /* set if we need to free password */
314 int user_result = SASL_OK;
315 int auth_result = SASL_OK;
316 int pass_result = SASL_OK;
317 int result;
318
319 *clientout = NULL;
320 *clientoutlen = 0;
321
322 /* doesn't really matter how the server responds */
323
324 /* check if sec layer strong enough */
325 if (params->props.min_ssf > params->external_ssf) {
326 #ifdef _INTEGRATED_SOLARIS_
327 SETERROR( params->utils, gettext("SSF requested of PLAIN plugin"));
328 #else
329 SETERROR( params->utils, "SSF requested of PLAIN plugin");
330 #endif /* _INTEGRATED_SOLARIS_ */
331 return SASL_TOOWEAK;
332 }
333
334 /* try to get the authid */
335 if (oparams->authid == NULL) {
336 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
337
338 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
339 return auth_result;
340 }
341
342 /* try to get the userid */
343 if (oparams->user == NULL) {
344 user_result = _plug_get_userid(params->utils, &user, prompt_need);
345
346 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
347 return user_result;
348 }
349
350 /* try to get the password */
351 if (password == NULL) {
352 pass_result = _plug_get_password(params->utils, &password,
353 &free_password, prompt_need);
354
355 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
356 return pass_result;
357 }
358
359 /* free prompts we got */
360 if (prompt_need && *prompt_need) {
361 params->utils->free(*prompt_need);
362 *prompt_need = NULL;
363 }
364
365 /* if there are prompts not filled in */
366 if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
367 (pass_result == SASL_INTERACT)) {
368 /* make the prompt list */
369 result =
370 #ifdef _INTEGRATED_SOLARIS_
371 _plug_make_prompts(params->utils, &text->h, prompt_need,
372 user_result == SASL_INTERACT ?
373 convert_prompt(params->utils, &text->h,
374 gettext("Please enter your authorization name"))
375 : NULL,
376 NULL,
377 auth_result == SASL_INTERACT ?
378 convert_prompt(params->utils, &text->h,
379 gettext("Please enter your authentication name"))
380 : NULL,
381 NULL,
382 pass_result == SASL_INTERACT ?
383 convert_prompt(params->utils, &text->h,
384 gettext("Please enter your password")) : NULL,
385 NULL,
386 NULL, NULL, NULL,
387 NULL, NULL, NULL);
388 #else
389 _plug_make_prompts(params->utils, prompt_need,
390 user_result == SASL_INTERACT ?
391 "Please enter your authorization name" : NULL,
392 NULL,
393 auth_result == SASL_INTERACT ?
394 "Please enter your authentication name" : NULL,
395 NULL,
396 pass_result == SASL_INTERACT ?
397 "Please enter your password" : NULL, NULL,
398 NULL, NULL, NULL,
399 NULL, NULL, NULL);
400 #endif /* _INTEGRATED_SOLARIS_ */
401 if (result != SASL_OK) goto cleanup;
402
403 return SASL_INTERACT;
404 }
405
406 if (!password) {
407 PARAMERROR(params->utils);
408 return SASL_BADPARAM;
409 }
410
411 if (!user || !*user) {
412 result = params->canon_user(params->utils->conn, authid, 0,
413 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
414 }
415 else {
416 result = params->canon_user(params->utils->conn, user, 0,
417 SASL_CU_AUTHZID, oparams);
418 if (result != SASL_OK) goto cleanup;
419
420 result = params->canon_user(params->utils->conn, authid, 0,
421 SASL_CU_AUTHID, oparams);
422 }
423 if (result != SASL_OK) goto cleanup;
424
425 /* send authorized id NUL authentication id NUL password */
426 *clientoutlen = (oparams->ulen + 1
427 + oparams->alen + 1
428 + password->len);
429
430 /* remember the extra NUL on the end for stupid clients */
431 result = _plug_buf_alloc(params->utils, &(text->out_buf),
432 &(text->out_buf_len), *clientoutlen + 1);
433 if (result != SASL_OK) goto cleanup;
434
435 memset(text->out_buf, 0, *clientoutlen + 1);
436 memcpy(text->out_buf, oparams->user, oparams->ulen);
437 memcpy(text->out_buf + oparams->ulen + 1, oparams->authid, oparams->alen);
438 memcpy(text->out_buf + oparams->ulen + oparams->alen + 2,
439 password->data, password->len);
440
441 *clientout = text->out_buf;
442
443 /* set oparams */
444 oparams->doneflag = 1;
445 oparams->mech_ssf = 0;
446 oparams->maxoutbuf = 0;
447 oparams->encode_context = NULL;
448 oparams->encode = NULL;
449 oparams->decode_context = NULL;
450 oparams->decode = NULL;
451 oparams->param_version = 0;
452
453 result = SASL_OK;
454
455 cleanup:
456 /* free sensitive info */
457 if (free_password) _plug_free_secret(params->utils, &password);
458
459 return result;
460 }
461
plain_client_mech_dispose(void * conn_context,const sasl_utils_t * utils)462 static void plain_client_mech_dispose(void *conn_context,
463 const sasl_utils_t *utils)
464 {
465 client_context_t *text = (client_context_t *) conn_context;
466
467 if (!text) return;
468
469 if (text->out_buf) utils->free(text->out_buf);
470 #ifdef _INTEGRATED_SOLARIS_
471 convert_prompt(utils, &text->h, NULL);
472 #endif /* _INTEGRATED_SOLARIS_ */
473
474 utils->free(text);
475 }
476
477 static sasl_client_plug_t plain_client_plugins[] =
478 {
479 {
480 "PLAIN", /* mech_name */
481 0, /* max_ssf */
482 SASL_SEC_NOANONYMOUS, /* security_flags */
483 SASL_FEAT_WANT_CLIENT_FIRST
484 | SASL_FEAT_ALLOWS_PROXY, /* features */
485 NULL, /* required_prompts */
486 NULL, /* glob_context */
487 &plain_client_mech_new, /* mech_new */
488 &plain_client_mech_step, /* mech_step */
489 &plain_client_mech_dispose, /* mech_dispose */
490 NULL, /* mech_free */
491 NULL, /* idle */
492 NULL, /* spare */
493 NULL /* spare */
494 }
495 };
496
plain_client_plug_init(sasl_utils_t * utils,int maxversion,int * out_version,sasl_client_plug_t ** pluglist,int * plugcount)497 int plain_client_plug_init(sasl_utils_t *utils,
498 int maxversion,
499 int *out_version,
500 sasl_client_plug_t **pluglist,
501 int *plugcount)
502 {
503 if (maxversion < SASL_CLIENT_PLUG_VERSION) {
504 SETERROR(utils, "PLAIN version mismatch");
505 return SASL_BADVERS;
506 }
507
508 *out_version = SASL_CLIENT_PLUG_VERSION;
509 *pluglist = plain_client_plugins;
510 *plugcount = 1;
511
512 return SASL_OK;
513 }
514