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