1 /*
2 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
3 */
4
5 /* LOGIN is a PLAIN-like authenticator, but for older deployments. */
6
7 /* Login SASL plugin
8 * Rob Siemborski (SASLv2 Conversion)
9 * contributed by Rainer Schoepf <schoepf@uni-mainz.de>
10 * based on PLAIN, by Tim Martin <tmartin@andrew.cmu.edu>
11 * $Id: login.c,v 1.25 2003/02/13 19:56:04 rjs3 Exp $
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 <ctype.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 "saslLOGIN.h"
65 #endif /* WIN32 */
66 #endif /* !_SUN_SDK_ */
67
68 /***************************** Common Section *****************************/
69
70 #ifndef _SUN_SDK_
71 static const char plugin_id[] = "$Id: login.c,v 1.25 2003/02/13 19:56:04 rjs3 Exp $";
72 #endif /* !_SUN_SDK_ */
73
74 /***************************** Server Section *****************************/
75
76 typedef struct context {
77 int state;
78
79 char *username;
80 size_t username_len;
81 } server_context_t;
82
login_server_mech_new(void * glob_context,sasl_server_params_t * sparams,const char * challenge,unsigned challen,void ** conn_context)83 static int login_server_mech_new(void *glob_context __attribute__((unused)),
84 sasl_server_params_t *sparams,
85 const char *challenge __attribute__((unused)),
86 unsigned challen __attribute__((unused)),
87 void **conn_context)
88 {
89 server_context_t *text;
90
91 /* holds state are in */
92 text = sparams->utils->malloc(sizeof(server_context_t));
93 if (text == NULL) {
94 MEMERROR( sparams->utils );
95 return SASL_NOMEM;
96 }
97
98 memset(text, 0, sizeof(server_context_t));
99
100 text->state = 1;
101
102 *conn_context = text;
103
104 return SASL_OK;
105 }
106
107 #define USERNAME_CHALLENGE "Username:"
108 #define PASSWORD_CHALLENGE "Password:"
109
login_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)110 static int login_server_mech_step(void *conn_context,
111 sasl_server_params_t *params,
112 const char *clientin,
113 unsigned clientinlen,
114 const char **serverout,
115 unsigned *serveroutlen,
116 sasl_out_params_t *oparams)
117 {
118 server_context_t *text = (server_context_t *) conn_context;
119
120 *serverout = NULL;
121 *serveroutlen = 0;
122
123 switch (text->state) {
124
125 case 1:
126 text->state = 2;
127
128 /* Check inlen, (possibly we have already the user name) */
129 /* In this case fall through to state 2 */
130 if (clientinlen == 0) {
131 /* demand username */
132
133 *serveroutlen = strlen(USERNAME_CHALLENGE);
134 *serverout = USERNAME_CHALLENGE;
135
136 return SASL_CONTINUE;
137 }
138
139
140 case 2:
141 /* Catch really long usernames */
142 if (clientinlen > 1024) {
143 #ifdef _SUN_SDK_
144 params->utils->log(params->utils->conn, SASL_LOG_ERR,
145 "username too long (>1024 characters)");
146 #else
147 SETERROR(params->utils, "username too long (>1024 characters)");
148 #endif /* _SUN_SDK_ */
149 return SASL_BADPROT;
150 }
151
152 /* get username */
153 text->username =
154 params->utils->malloc(sizeof(sasl_secret_t) + clientinlen + 1);
155 if (!text->username) {
156 MEMERROR( params->utils );
157 return SASL_NOMEM;
158 }
159
160 strncpy(text->username, clientin, clientinlen);
161 text->username_len = clientinlen;
162 text->username[clientinlen] = '\0';
163
164 /* demand password */
165 *serveroutlen = strlen(PASSWORD_CHALLENGE);
166 *serverout = PASSWORD_CHALLENGE;
167
168 text->state = 3;
169
170 return SASL_CONTINUE;
171
172
173 case 3: {
174 sasl_secret_t *password;
175 int result;
176
177 /* Catch really long passwords */
178 if (clientinlen > 1024) {
179 #ifdef _SUN_SDK_
180 params->utils->log(params->utils->conn, SASL_LOG_ERR,
181 "clientinlen is > 1024 characters in LOGIN plugin");
182 #else
183 SETERROR(params->utils,
184 "clientinlen is > 1024 characters in LOGIN plugin");
185 #endif /* _SUN_SDK_ */
186 return SASL_BADPROT;
187 }
188
189 /* get password */
190 password =
191 params->utils->malloc(sizeof(sasl_secret_t) + clientinlen + 1);
192 if (!password) {
193 MEMERROR(params->utils);
194 return SASL_NOMEM;
195 }
196
197 strncpy((char *)password->data, clientin, clientinlen);
198 password->data[clientinlen] = '\0';
199 password->len = clientinlen;
200
201 /* canonicalize username first, so that password verification is
202 * done against the canonical id */
203 result = params->canon_user(params->utils->conn, text->username,
204 text->username_len,
205 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
206 if (result != SASL_OK) {
207 _plug_free_secret(params->utils, &password);
208 return result;
209 }
210
211 /* verify_password - return sasl_ok on success */
212 result = params->utils->checkpass(params->utils->conn,
213 oparams->authid, oparams->alen,
214 (char *)password->data, password->len);
215
216 if (result != SASL_OK) {
217 _plug_free_secret(params->utils, &password);
218 return result;
219 }
220
221 if (params->transition) {
222 params->transition(params->utils->conn,
223 (char *)password->data, password->len);
224 }
225
226 _plug_free_secret(params->utils, &password);
227
228 *serverout = NULL;
229 *serveroutlen = 0;
230
231 oparams->doneflag = 1;
232 oparams->mech_ssf = 0;
233 oparams->maxoutbuf = 0;
234 oparams->encode_context = NULL;
235 oparams->encode = NULL;
236 oparams->decode_context = NULL;
237 oparams->decode = NULL;
238 oparams->param_version = 0;
239
240 return SASL_OK;
241 }
242
243
244 default:
245 params->utils->log(NULL, SASL_LOG_ERR,
246 "Invalid LOGIN server step %d\n", text->state);
247 return SASL_FAIL;
248 }
249
250 return SASL_FAIL; /* should never get here */
251 }
252
login_server_mech_dispose(void * conn_context,const sasl_utils_t * utils)253 static void login_server_mech_dispose(void *conn_context,
254 const sasl_utils_t *utils)
255 {
256 server_context_t *text = (server_context_t *) conn_context;
257
258 if (!text) return;
259
260 if (text->username) utils->free(text->username);
261
262 utils->free(text);
263 }
264
265 static sasl_server_plug_t login_server_plugins[] =
266 {
267 {
268 "LOGIN", /* mech_name */
269 0, /* max_ssf */
270 SASL_SEC_NOANONYMOUS, /* security_flags */
271 0, /* features */
272 NULL, /* glob_context */
273 &login_server_mech_new, /* mech_new */
274 &login_server_mech_step, /* mech_step */
275 &login_server_mech_dispose, /* mech_dispose */
276 NULL, /* mech_free */
277 NULL, /* setpass */
278 NULL, /* user_query */
279 NULL, /* idle */
280 NULL, /* mech_avail */
281 NULL /* spare */
282 }
283 };
284
login_server_plug_init(sasl_utils_t * utils,int maxversion,int * out_version,sasl_server_plug_t ** pluglist,int * plugcount)285 int login_server_plug_init(sasl_utils_t *utils,
286 int maxversion,
287 int *out_version,
288 sasl_server_plug_t **pluglist,
289 int *plugcount)
290 {
291 if (maxversion < SASL_SERVER_PLUG_VERSION) {
292 SETERROR(utils, "LOGIN version mismatch");
293 return SASL_BADVERS;
294 }
295
296 *out_version = SASL_SERVER_PLUG_VERSION;
297 *pluglist = login_server_plugins;
298 *plugcount = 1;
299
300 return SASL_OK;
301 }
302
303 /***************************** Client Section *****************************/
304
305 typedef struct client_context {
306 int state;
307
308 #ifdef _INTEGRATED_SOLARIS_
309 void *h;
310 #endif /* _INTEGRATED_SOLARIS_ */
311 sasl_secret_t *password;
312 unsigned int free_password; /* set if we need to free password */
313 } client_context_t;
314
login_client_mech_new(void * glob_context,sasl_client_params_t * params,void ** conn_context)315 static int login_client_mech_new(void *glob_context __attribute__((unused)),
316 sasl_client_params_t *params,
317 void **conn_context)
318 {
319 client_context_t *text;
320
321 /* holds state are in */
322 text = params->utils->malloc(sizeof(client_context_t));
323 if (text == NULL) {
324 MEMERROR(params->utils);
325 return SASL_NOMEM;
326 }
327
328 memset(text, 0, sizeof(client_context_t));
329
330 text->state = 1;
331
332 *conn_context = text;
333
334 return SASL_OK;
335 }
336
login_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)337 static int login_client_mech_step(void *conn_context,
338 sasl_client_params_t *params,
339 const char *serverin __attribute__((unused)),
340 unsigned serverinlen __attribute__((unused)),
341 sasl_interact_t **prompt_need,
342 const char **clientout,
343 unsigned *clientoutlen,
344 sasl_out_params_t *oparams)
345 {
346 client_context_t *text = (client_context_t *) conn_context;
347
348 *clientout = NULL;
349 *clientoutlen = 0;
350
351 switch (text->state) {
352
353 case 1: {
354 const char *user;
355 int auth_result = SASL_OK;
356 int pass_result = SASL_OK;
357 int result;
358
359 /* check if sec layer strong enough */
360 if (params->props.min_ssf > params->external_ssf) {
361 #ifdef _INTEGRATED_SOLARIS_
362 params->utils->log(params->utils->conn, SASL_LOG_ERR,
363 gettext("SSF requested of LOGIN plugin"));
364 #else
365 SETERROR( params->utils, "SSF requested of LOGIN plugin");
366 #endif /* _INTEGRATED_SOLARIS_ */
367 return SASL_TOOWEAK;
368 }
369
370 /* try to get the userid */
371 /* Note: we want to grab the authname and not the userid, which is
372 * who we AUTHORIZE as, and will be the same as the authname
373 * for the LOGIN mech.
374 */
375 if (oparams->user == NULL) {
376 auth_result = _plug_get_authid(params->utils, &user, prompt_need);
377
378 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
379 return auth_result;
380 }
381
382 /* try to get the password */
383 if (text->password == NULL) {
384 pass_result = _plug_get_password(params->utils, &text->password,
385 &text->free_password, prompt_need);
386
387 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
388 return pass_result;
389 }
390
391 /* free prompts we got */
392 if (prompt_need && *prompt_need) {
393 params->utils->free(*prompt_need);
394 *prompt_need = NULL;
395 }
396
397 /* if there are prompts not filled in */
398 if ((auth_result == SASL_INTERACT) || (pass_result == SASL_INTERACT)) {
399 /* make the prompt list */
400 result =
401 #ifdef _INTEGRATED_SOLARIS_
402 _plug_make_prompts(params->utils, &text->h, prompt_need,
403 NULL, NULL,
404 auth_result == SASL_INTERACT ?
405 gettext("Please enter your authentication name") : NULL,
406 NULL,
407 pass_result == SASL_INTERACT ?
408 gettext("Please enter your password") : NULL, NULL,
409 NULL, NULL, NULL,
410 NULL, NULL, NULL);
411 #else
412 _plug_make_prompts(params->utils, prompt_need,
413 NULL, NULL,
414 auth_result == SASL_INTERACT ?
415 "Please enter your authentication name" : NULL,
416 NULL,
417 pass_result == SASL_INTERACT ?
418 "Please enter your password" : NULL, NULL,
419 NULL, NULL, NULL,
420 NULL, NULL, NULL);
421 #endif /* _INTEGRATED_SOLARIS_ */
422 if (result != SASL_OK) return result;
423
424 return SASL_INTERACT;
425 }
426
427 if (!text->password) {
428 PARAMERROR(params->utils);
429 return SASL_BADPARAM;
430 }
431
432 result = params->canon_user(params->utils->conn, user, 0,
433 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
434 if (result != SASL_OK) return result;
435
436 /* server should have sent request for username - we ignore it */
437 if (!serverin) {
438 #ifdef _SUN_SDK_
439 params->utils->log(params->utils->conn, SASL_LOG_ERR,
440 "Server didn't issue challenge for USERNAME");
441 #else
442 SETERROR( params->utils,
443 "Server didn't issue challenge for USERNAME");
444 #endif /* _SUN_SDK_ */
445 return SASL_BADPROT;
446 }
447
448 if (!clientout) {
449 PARAMERROR( params->utils );
450 return SASL_BADPARAM;
451 }
452
453 if (clientoutlen) *clientoutlen = oparams->alen;
454 *clientout = oparams->authid;
455
456 text->state = 2;
457
458 return SASL_CONTINUE;
459 }
460
461 case 2:
462 /* server should have sent request for password - we ignore it */
463 if (!serverin) {
464 #ifdef _SUN_SDK_
465 params->utils->log(params->utils->conn, SASL_LOG_ERR,
466 "Server didn't issue challenge for PASSWORD");
467 #else
468 SETERROR( params->utils,
469 "Server didn't issue challenge for PASSWORD");
470 #endif /* _SUN_SDK_ */
471 return SASL_BADPROT;
472 }
473
474 if (!clientout) {
475 PARAMERROR(params->utils);
476 return SASL_BADPARAM;
477 }
478
479 if (clientoutlen) *clientoutlen = text->password->len;
480 *clientout = (char *)text->password->data;
481
482 /* set oparams */
483 oparams->doneflag = 1;
484 oparams->mech_ssf = 0;
485 oparams->maxoutbuf = 0;
486 oparams->encode_context = NULL;
487 oparams->encode = NULL;
488 oparams->decode_context = NULL;
489 oparams->decode = NULL;
490 oparams->param_version = 0;
491
492 return SASL_OK;
493
494 default:
495 params->utils->log(NULL, SASL_LOG_ERR,
496 "Invalid LOGIN client step %d\n", text->state);
497 return SASL_FAIL;
498 }
499
500 return SASL_FAIL; /* should never get here */
501 }
502
login_client_mech_dispose(void * conn_context,const sasl_utils_t * utils)503 static void login_client_mech_dispose(void *conn_context,
504 const sasl_utils_t *utils)
505 {
506 client_context_t *text = (client_context_t *) conn_context;
507
508 if (!text) return;
509
510 /* free sensitive info */
511 if (text->free_password) _plug_free_secret(utils, &(text->password));
512 #ifdef _INTEGRATED_SOLARIS_
513 convert_prompt(utils, &text->h, NULL);
514 #endif /* _INTEGRATED_SOLARIS_ */
515
516 utils->free(text);
517 }
518
519 static sasl_client_plug_t login_client_plugins[] =
520 {
521 {
522 "LOGIN", /* mech_name */
523 0, /* max_ssf */
524 SASL_SEC_NOANONYMOUS, /* security_flags */
525 SASL_FEAT_SERVER_FIRST, /* features */
526 NULL, /* required_prompts */
527 NULL, /* glob_context */
528 &login_client_mech_new, /* mech_new */
529 &login_client_mech_step, /* mech_step */
530 &login_client_mech_dispose, /* mech_dispose */
531 NULL, /* mech_free */
532 NULL, /* idle */
533 NULL, /* spare */
534 NULL /* spare */
535 }
536 };
537
login_client_plug_init(sasl_utils_t * utils,int maxversion,int * out_version,sasl_client_plug_t ** pluglist,int * plugcount)538 int login_client_plug_init(sasl_utils_t *utils,
539 int maxversion,
540 int *out_version,
541 sasl_client_plug_t **pluglist,
542 int *plugcount)
543 {
544 if (maxversion < SASL_CLIENT_PLUG_VERSION) {
545 SETERROR(utils, "Version mismatch in LOGIN");
546 return SASL_BADVERS;
547 }
548
549 *out_version = SASL_CLIENT_PLUG_VERSION;
550 *pluglist = login_client_plugins;
551 *plugcount = 1;
552
553 return SASL_OK;
554 }
555