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