1/* Plain SASL plugin 2 * Rob Siemborski 3 * Tim Martin 4 * $Id: plain.c,v 1.7 2006/02/03 22:33:14 snsimon 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 60//static const char plugin_id[] = "$Id: plain.c,v 1.7 2006/02/03 22:33:14 snsimon 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 size_t password_len; 93 unsigned lup=0; 94 int result; 95 char *passcopy; 96 97 *serverout = NULL; 98 *serveroutlen = 0; 99 100 /* should have received author-id NUL authen-id NUL password */ 101 102 /* get author */ 103 author = clientin; 104 while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; 105 106 if (lup >= clientinlen) { 107 SETERROR(params->utils, "Can only find author (no password)"); 108 return SASL_BADPROT; 109 } 110 111 /* get authen */ 112 ++lup; 113 authen = clientin + lup; 114 while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; 115 116 if (lup >= clientinlen) { 117 params->utils->seterror(params->utils->conn, 0, 118 "Can only find author/en (no password)"); 119 return SASL_BADPROT; 120 } 121 122 /* get password */ 123 lup++; 124 password = clientin + lup; 125 while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; 126 127 password_len = clientin + lup - password; 128 129 if (lup != clientinlen) { 130 SETERROR(params->utils, 131 "Got more data than we were expecting in the PLAIN plugin\n"); 132 return SASL_BADPROT; 133 } 134 135 /* this kinda sucks. we need password to be null terminated 136 but we can't assume there is an allocated byte at the end 137 of password so we have to copy it */ 138 passcopy = params->utils->malloc(password_len + 1); 139 if (passcopy == NULL) { 140 MEMERROR(params->utils); 141 return SASL_NOMEM; 142 } 143 144 strncpy(passcopy, password, password_len); 145 passcopy[password_len] = '\0'; 146 147 /* Canonicalize userid first, so that password verification is only 148 * against the canonical id */ 149 if (!author || !*author) 150 author = authen; 151 152 result = params->canon_user(params->utils->conn, 153 authen, 0, SASL_CU_AUTHID, oparams); 154 if (result != SASL_OK) { 155 _plug_free_string(params->utils, &passcopy); 156 return result; 157 } 158 159 /* verify password - return sasl_ok on success*/ 160 result = params->utils->checkpass(params->utils->conn, 161 oparams->authid, oparams->alen, 162 passcopy, password_len); 163 164 _plug_free_string(params->utils, &passcopy); 165 166 if (result != SASL_OK) { 167 params->utils->seterror(params->utils->conn, 0, 168 "Password verification failed"); 169 return result; 170 } 171 172 /* Canonicalize and store the authorization ID */ 173 /* We need to do this after calling verify_user just in case verify_user 174 * needed to get auxprops itself */ 175 result = params->canon_user(params->utils->conn, 176 author, 0, SASL_CU_AUTHZID, oparams); 177 if (result != SASL_OK) return result; 178 179 /* set oparams */ 180 oparams->doneflag = 1; 181 oparams->mech_ssf = 0; 182 oparams->maxoutbuf = 0; 183 oparams->encode_context = NULL; 184 oparams->encode = NULL; 185 oparams->decode_context = NULL; 186 oparams->decode = NULL; 187 oparams->param_version = 0; 188 189 return SASL_OK; 190} 191 192static sasl_server_plug_t plain_server_plugins[] = 193{ 194 { 195 "PLAIN", /* mech_name */ 196 0, /* max_ssf */ 197 SASL_SEC_NOANONYMOUS, /* security_flags */ 198 SASL_FEAT_WANT_CLIENT_FIRST 199 | SASL_FEAT_ALLOWS_PROXY, /* features */ 200 NULL, /* glob_context */ 201 &plain_server_mech_new, /* mech_new */ 202 &plain_server_mech_step, /* mech_step */ 203 NULL, /* mech_dispose */ 204 NULL, /* mech_free */ 205 NULL, /* setpass */ 206 NULL, /* user_query */ 207 NULL, /* idle */ 208 NULL, /* mech_avail */ 209 NULL /* spare */ 210 } 211}; 212 213int plain_server_plug_init(const sasl_utils_t *utils, 214 int maxversion, 215 int *out_version, 216 sasl_server_plug_t **pluglist, 217 int *plugcount) 218{ 219 if (maxversion < SASL_SERVER_PLUG_VERSION) { 220 SETERROR(utils, "PLAIN version mismatch"); 221 return SASL_BADVERS; 222 } 223 224 *out_version = SASL_SERVER_PLUG_VERSION; 225 *pluglist = plain_server_plugins; 226 *plugcount = 1; 227 228 return SASL_OK; 229} 230 231/***************************** Client Section *****************************/ 232 233typedef struct client_context { 234 char *out_buf; 235 unsigned out_buf_len; 236} client_context_t; 237 238static int plain_client_mech_new(void *glob_context __attribute__((unused)), 239 sasl_client_params_t *params, 240 void **conn_context) 241{ 242 client_context_t *text; 243 244 /* holds state are in */ 245 text = params->utils->malloc(sizeof(client_context_t)); 246 if (text == NULL) { 247 MEMERROR( params->utils ); 248 return SASL_NOMEM; 249 } 250 251 memset(text, 0, sizeof(client_context_t)); 252 253 *conn_context = text; 254 255 return SASL_OK; 256} 257 258static int plain_client_mech_step(void *conn_context, 259 sasl_client_params_t *params, 260 const char *serverin __attribute__((unused)), 261 unsigned serverinlen __attribute__((unused)), 262 sasl_interact_t **prompt_need, 263 const char **clientout, 264 unsigned *clientoutlen, 265 sasl_out_params_t *oparams) 266{ 267 client_context_t *text = (client_context_t *) conn_context; 268 const char *user = NULL, *authid = NULL; 269 sasl_secret_t *password = NULL; 270 unsigned int free_password = 0; /* set if we need to free password */ 271 int user_result = SASL_OK; 272 int auth_result = SASL_OK; 273 int pass_result = SASL_OK; 274 int result; 275 char *p; 276 277 *clientout = NULL; 278 *clientoutlen = 0; 279 280 /* doesn't really matter how the server responds */ 281 282 /* check if sec layer strong enough */ 283 if (params->props.min_ssf > params->external_ssf) { 284 SETERROR( params->utils, "SSF requested of PLAIN plugin"); 285 return SASL_TOOWEAK; 286 } 287 288 /* try to get the authid */ 289 if (oparams->authid == NULL) { 290 auth_result = _plug_get_authid(params->utils, &authid, prompt_need); 291 292 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) 293 return auth_result; 294 } 295 296 /* try to get the userid */ 297 if (oparams->user == NULL) { 298 user_result = _plug_get_userid(params->utils, &user, prompt_need); 299 300 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) 301 return user_result; 302 } 303 304 /* try to get the password */ 305 if (password == NULL) { 306 pass_result = _plug_get_password(params->utils, &password, 307 &free_password, prompt_need); 308 309 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) 310 return pass_result; 311 } 312 313 /* free prompts we got */ 314 if (prompt_need && *prompt_need) { 315 params->utils->free(*prompt_need); 316 *prompt_need = NULL; 317 } 318 319 /* if there are prompts not filled in */ 320 if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) || 321 (pass_result == SASL_INTERACT)) { 322 /* make the prompt list */ 323 result = 324 _plug_make_prompts(params->utils, prompt_need, 325 user_result == SASL_INTERACT ? 326 "Please enter your authorization name" : NULL, 327 NULL, 328 auth_result == SASL_INTERACT ? 329 "Please enter your authentication name" : NULL, 330 NULL, 331 pass_result == SASL_INTERACT ? 332 "Please enter your password" : NULL, NULL, 333 NULL, NULL, NULL, 334 NULL, NULL, NULL); 335 if (result != SASL_OK) goto cleanup; 336 337 return SASL_INTERACT; 338 } 339 340 if (!password) { 341 PARAMERROR(params->utils); 342 return SASL_BADPARAM; 343 } 344 345 if (!user || !*user) { 346 result = params->canon_user(params->utils->conn, authid, 0, 347 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); 348 } 349 else { 350 result = params->canon_user(params->utils->conn, user, 0, 351 SASL_CU_AUTHZID, oparams); 352 if (result != SASL_OK) goto cleanup; 353 354 result = params->canon_user(params->utils->conn, authid, 0, 355 SASL_CU_AUTHID, oparams); 356 } 357 if (result != SASL_OK) goto cleanup; 358 359 /* send authorized id NUL authentication id NUL password */ 360 *clientoutlen = ((user && *user ? oparams->ulen : 0) + 361 1 + oparams->alen + 362 1 + password->len); 363 364 /* remember the extra NUL on the end for stupid clients */ 365 result = _plug_buf_alloc(params->utils, (unsigned char **)&(text->out_buf), 366 &(text->out_buf_len), *clientoutlen + 1); 367 if (result != SASL_OK) goto cleanup; 368 369 memset(text->out_buf, 0, *clientoutlen + 1); 370 p = text->out_buf; 371 if (user && *user) { 372 memcpy(p, oparams->user, oparams->ulen); 373 p += oparams->ulen; 374 } 375 memcpy(++p, oparams->authid, oparams->alen); 376 p += oparams->alen; 377 memcpy(++p, password->data, password->len); 378 379 *clientout = text->out_buf; 380 381 /* set oparams */ 382 oparams->doneflag = 1; 383 oparams->mech_ssf = 0; 384 oparams->maxoutbuf = 0; 385 oparams->encode_context = NULL; 386 oparams->encode = NULL; 387 oparams->decode_context = NULL; 388 oparams->decode = NULL; 389 oparams->param_version = 0; 390 391 result = SASL_OK; 392 393 cleanup: 394 /* free sensitive info */ 395 if (free_password) _plug_free_secret(params->utils, &password); 396 397 return result; 398} 399 400static void plain_client_mech_dispose(void *conn_context, 401 const sasl_utils_t *utils) 402{ 403 client_context_t *text = (client_context_t *) conn_context; 404 405 if (!text) return; 406 407 if (text->out_buf) utils->free(text->out_buf); 408 409 utils->free(text); 410} 411 412static sasl_client_plug_t plain_client_plugins[] = 413{ 414 { 415 "PLAIN", /* mech_name */ 416 0, /* max_ssf */ 417 SASL_SEC_NOANONYMOUS, /* security_flags */ 418 SASL_FEAT_WANT_CLIENT_FIRST 419 | SASL_FEAT_ALLOWS_PROXY, /* features */ 420 NULL, /* required_prompts */ 421 NULL, /* glob_context */ 422 &plain_client_mech_new, /* mech_new */ 423 &plain_client_mech_step, /* mech_step */ 424 &plain_client_mech_dispose, /* mech_dispose */ 425 NULL, /* mech_free */ 426 NULL, /* idle */ 427 NULL, /* spare */ 428 NULL /* spare */ 429 } 430}; 431 432int plain_client_plug_init(sasl_utils_t *utils, 433 int maxversion, 434 int *out_version, 435 sasl_client_plug_t **pluglist, 436 int *plugcount) 437{ 438 if (maxversion < SASL_CLIENT_PLUG_VERSION) { 439 SETERROR(utils, "PLAIN version mismatch"); 440 return SASL_BADVERS; 441 } 442 443 *out_version = SASL_CLIENT_PLUG_VERSION; 444 *pluglist = plain_client_plugins; 445 *plugcount = 1; 446 447 return SASL_OK; 448} 449