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