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 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 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 /* FALLTHROUGH */ 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 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 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 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 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 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 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