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 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 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 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 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 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 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 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