1/* Generic SASL plugin utility functions 2 * Rob Siemborski 3 * $Id: plugin_common.c,v 1.3 2006/02/03 22:33:14 snsimon Exp $ 4 */ 5/* 6 * Copyright (c) 2001 Carnegie Mellon University. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 20 * 3. The name "Carnegie Mellon University" must not be used to 21 * endorse or promote products derived from this software without 22 * prior written permission. For permission or any other legal 23 * details, please contact 24 * Office of Technology Transfer 25 * Carnegie Mellon University 26 * 5000 Forbes Avenue 27 * Pittsburgh, PA 15213-3890 28 * (412) 268-4387, fax: (412) 268-7395 29 * tech-transfer@andrew.cmu.edu 30 * 31 * 4. Redistributions of any form whatsoever must retain the following 32 * acknowledgment: 33 * "This product includes software developed by Computing Services 34 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 35 * 36 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 37 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 38 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 39 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 40 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 41 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 42 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 43 */ 44 45#include <config.h> 46#ifndef macintosh 47#ifdef WIN32 48# include <winsock.h> 49#else 50# include <sys/param.h> 51# include <sys/socket.h> 52# include <netinet/in.h> 53# include <arpa/inet.h> 54# include <netdb.h> 55#endif /* WIN32 */ 56#endif /* macintosh */ 57#ifdef HAVE_UNISTD_H 58#include <unistd.h> 59#endif 60#include <fcntl.h> 61#include <sasl.h> 62#include <saslutil.h> 63#include <saslplug.h> 64 65#include <errno.h> 66#include <ctype.h> 67 68#ifdef HAVE_INTTYPES_H 69#include <inttypes.h> 70#endif 71 72#include "plugin_common.h" 73 74/* translate IPv4 mapped IPv6 address to IPv4 address */ 75static void sockaddr_unmapped( 76#ifdef IN6_IS_ADDR_V4MAPPED 77 struct sockaddr *sa, socklen_t *len 78#else 79 struct sockaddr *sa __attribute__((unused)), 80 socklen_t *len __attribute__((unused)) 81#endif 82) 83{ 84#ifdef IN6_IS_ADDR_V4MAPPED 85 struct sockaddr_in6 *sin6; 86 struct sockaddr_in *sin4; 87 uint32_t addr; 88 int port; 89 90 if (sa->sa_family != AF_INET6) 91 return; 92 sin6 = (struct sockaddr_in6 *)sa; 93 if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 94 return; 95 sin4 = (struct sockaddr_in *)sa; 96 addr = *(uint32_t *)&sin6->sin6_addr.s6_addr[12]; 97 port = sin6->sin6_port; 98 memset(sin4, 0, sizeof(struct sockaddr_in)); 99 sin4->sin_addr.s_addr = addr; 100 sin4->sin_port = port; 101 sin4->sin_family = AF_INET; 102#ifdef HAVE_SOCKADDR_SA_LEN 103 sin4->sin_len = sizeof(struct sockaddr_in); 104#endif 105 *len = sizeof(struct sockaddr_in); 106#else 107 return; 108#endif 109} 110 111int _plug_ipfromstring(const sasl_utils_t *utils, const char *addr, 112 struct sockaddr *out, socklen_t outlen) 113{ 114 int i, j; 115 socklen_t len; 116 struct sockaddr_storage ss; 117 struct addrinfo hints, *ai = NULL; 118 char hbuf[NI_MAXHOST]; 119 120 if(!utils || !addr || !out) { 121 if(utils) PARAMERROR( utils ); 122 return SASL_BADPARAM; 123 } 124 125 /* Parse the address */ 126 for (i = 0; addr[i] != '\0' && addr[i] != ';'; i++) { 127 if (i >= NI_MAXHOST) { 128 if(utils) PARAMERROR( utils ); 129 return SASL_BADPARAM; 130 } 131 hbuf[i] = addr[i]; 132 } 133 hbuf[i] = '\0'; 134 135 if (addr[i] == ';') 136 i++; 137 /* XXX/FIXME: Do we need this check? */ 138 for (j = i; addr[j] != '\0'; j++) 139 if (!isdigit((int)(addr[j]))) { 140 PARAMERROR( utils ); 141 return SASL_BADPARAM; 142 } 143 144 memset(&hints, 0, sizeof(hints)); 145 hints.ai_family = PF_UNSPEC; 146 hints.ai_socktype = SOCK_STREAM; 147 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; 148 149 if (getaddrinfo(hbuf, &addr[i], &hints, &ai) != 0) { 150 PARAMERROR( utils ); 151 return SASL_BADPARAM; 152 } 153 154 len = ai->ai_addrlen; 155 memcpy(&ss, ai->ai_addr, len); 156 freeaddrinfo(ai); 157 sockaddr_unmapped((struct sockaddr *)&ss, &len); 158 if (outlen < len) { 159 PARAMERROR( utils ); 160 return SASL_BUFOVER; 161 } 162 163 memcpy(out, &ss, len); 164 165 return SASL_OK; 166} 167 168int _plug_iovec_to_buf(const sasl_utils_t *utils, const struct iovec *vec, 169 unsigned numiov, buffer_info_t **output) 170{ 171 unsigned i; 172 int ret; 173 buffer_info_t *out; 174 unsigned char *pos; 175 176 if(!utils || !vec || !output) { 177 if(utils) PARAMERROR( utils ); 178 return SASL_BADPARAM; 179 } 180 181 if(!(*output)) { 182 *output = utils->malloc(sizeof(buffer_info_t)); 183 if(!*output) { 184 MEMERROR(utils); 185 return SASL_NOMEM; 186 } 187 memset(*output,0,sizeof(buffer_info_t)); 188 } 189 190 out = *output; 191 192 out->curlen = 0; 193 for(i=0; i<numiov; i++) 194 out->curlen += vec[i].iov_len; 195 196 ret = _plug_buf_alloc(utils, &out->data, &out->reallen, out->curlen); 197 198 if(ret != SASL_OK) { 199 MEMERROR(utils); 200 return SASL_NOMEM; 201 } 202 203 memset(out->data, 0, out->reallen); 204 pos = out->data; 205 206 for(i=0; i<numiov; i++) { 207 memcpy(pos, vec[i].iov_base, vec[i].iov_len); 208 pos += vec[i].iov_len; 209 } 210 211 return SASL_OK; 212} 213 214/* Basically a conditional call to realloc(), if we need more */ 215int _plug_buf_alloc(const sasl_utils_t *utils, unsigned char **rwbuf, 216 unsigned int *curlen, unsigned int newlen) 217{ 218 if(!utils || !rwbuf || !curlen) { 219 PARAMERROR(utils); 220 return SASL_BADPARAM; 221 } 222 223 if(!(*rwbuf)) { 224 *rwbuf = utils->malloc(newlen); 225 if (*rwbuf == NULL) { 226 *curlen = 0; 227 MEMERROR(utils); 228 return SASL_NOMEM; 229 } 230 *curlen = newlen; 231 } else if(*rwbuf && *curlen < newlen) { 232 size_t needed = 2*(*curlen); 233 234 while(needed < newlen) 235 needed *= 2; 236 237 *rwbuf = utils->realloc(*rwbuf, needed); 238 if (*rwbuf == NULL) { 239 *curlen = 0; 240 MEMERROR(utils); 241 return SASL_NOMEM; 242 } 243 *curlen = needed; 244 } 245 246 return SASL_OK; 247} 248 249/* copy a string */ 250int _plug_strdup(const sasl_utils_t * utils, const char *in, 251 char **out, int *outlen) 252{ 253 size_t len = strlen(in); 254 255 if(!utils || !in || !out) { 256 if(utils) PARAMERROR(utils); 257 return SASL_BADPARAM; 258 } 259 260 *out = utils->malloc(len + 1); 261 if (!*out) { 262 MEMERROR(utils); 263 return SASL_NOMEM; 264 } 265 266 strlcpy((char *) *out, in, len+1); 267 268 if (outlen) 269 *outlen = len; 270 271 return SASL_OK; 272} 273 274void _plug_free_string(const sasl_utils_t *utils, char **str) 275{ 276 size_t len; 277 278 if (!utils || !str || !(*str)) return; 279 280 len = strlen(*str); 281 282 utils->erasebuffer(*str, len); 283 utils->free(*str); 284 285 *str=NULL; 286} 287 288void _plug_free_secret(const sasl_utils_t *utils, sasl_secret_t **secret) 289{ 290 if(!utils || !secret || !(*secret)) return; 291 292 utils->erasebuffer((char *)(*secret)->data, (*secret)->len); 293 utils->free(*secret); 294 *secret = NULL; 295} 296 297/* 298 * Trys to find the prompt with the lookingfor id in the prompt list 299 * Returns it if found. NULL otherwise 300 */ 301sasl_interact_t *_plug_find_prompt(sasl_interact_t **promptlist, 302 unsigned int lookingfor) 303{ 304 sasl_interact_t *prompt; 305 306 if (promptlist && *promptlist) { 307 for (prompt = *promptlist; prompt->id != SASL_CB_LIST_END; ++prompt) { 308 if (prompt->id==lookingfor) 309 return prompt; 310 } 311 } 312 313 return NULL; 314} 315 316/* 317 * Retrieve the simple string given by the callback id. 318 */ 319int _plug_get_simple(const sasl_utils_t *utils, unsigned int id, 320 const char **result, sasl_interact_t **prompt_need) 321{ 322 323 int ret; 324 sasl_getsimple_t *simple_cb; 325 void *simple_context; 326 sasl_interact_t *prompt; 327 328 /* see if we were given the result in the prompt */ 329 prompt = _plug_find_prompt(prompt_need, id); 330 if (prompt != NULL) { 331 /* We prompted, and got.*/ 332 333 if (!prompt->result) { 334 SETERROR(utils, "Unexpectedly missing a prompt result"); 335 return SASL_BADPARAM; 336 } 337 338 *result = prompt->result; 339 return SASL_OK; 340 } 341 342 /* Try to get the callback... */ 343 ret = utils->getcallback(utils->conn, id, &simple_cb, &simple_context); 344 345 if (ret == SASL_OK && simple_cb) { 346 ret = simple_cb(simple_context, id, result, NULL); 347 if (ret != SASL_OK) 348 return ret; 349 350 if (!*result) { 351 PARAMERROR(utils); 352 return SASL_BADPARAM; 353 } 354 } 355 356 return ret; 357} 358 359/* 360 * Retrieve the user password. 361 */ 362int _plug_get_password(const sasl_utils_t *utils, sasl_secret_t **password, 363 unsigned int *iscopy, sasl_interact_t **prompt_need) 364{ 365 int ret; 366 sasl_getsecret_t *pass_cb; 367 void *pass_context; 368 sasl_interact_t *prompt; 369 370 *iscopy = 0; 371 372 /* see if we were given the password in the prompt */ 373 prompt = _plug_find_prompt(prompt_need, SASL_CB_PASS); 374 if (prompt != NULL) { 375 /* We prompted, and got.*/ 376 377 if (!prompt->result) { 378 SETERROR(utils, "Unexpectedly missing a prompt result"); 379 return SASL_BADPARAM; 380 } 381 382 /* copy what we got into a secret_t */ 383 *password = (sasl_secret_t *) utils->malloc(sizeof(sasl_secret_t) + 384 prompt->len + 1); 385 if (!*password) { 386 MEMERROR(utils); 387 return SASL_NOMEM; 388 } 389 390 (*password)->len=prompt->len; 391 memcpy((*password)->data, prompt->result, prompt->len); 392 (*password)->data[(*password)->len]=0; 393 394 *iscopy = 1; 395 396 return SASL_OK; 397 } 398 399 /* Try to get the callback... */ 400 ret = utils->getcallback(utils->conn, SASL_CB_PASS, 401 &pass_cb, &pass_context); 402 403 if (ret == SASL_OK && pass_cb) { 404 ret = pass_cb(utils->conn, pass_context, SASL_CB_PASS, password); 405 if (ret != SASL_OK) 406 return ret; 407 408 if (!*password) { 409 PARAMERROR(utils); 410 return SASL_BADPARAM; 411 } 412 } 413 414 return ret; 415} 416 417/* 418 * Retrieve the string given by the challenge prompt id. 419 */ 420int _plug_challenge_prompt(const sasl_utils_t *utils, unsigned int id, 421 const char *challenge, const char *promptstr, 422 const char **result, sasl_interact_t **prompt_need) 423{ 424 int ret; 425 sasl_chalprompt_t *chalprompt_cb; 426 void *chalprompt_context; 427 sasl_interact_t *prompt; 428 429 /* see if we were given the password in the prompt */ 430 prompt = _plug_find_prompt(prompt_need, id); 431 if (prompt != NULL) { 432 /* We prompted, and got.*/ 433 434 if (!prompt->result) { 435 SETERROR(utils, "Unexpectedly missing a prompt result"); 436 return SASL_BADPARAM; 437 } 438 439 *result = prompt->result; 440 return SASL_OK; 441 } 442 443 /* Try to get the callback... */ 444 ret = utils->getcallback(utils->conn, id, 445 &chalprompt_cb, &chalprompt_context); 446 447 if (ret == SASL_OK && chalprompt_cb) { 448 ret = chalprompt_cb(chalprompt_context, id, 449 challenge, promptstr, NULL, result, NULL); 450 if (ret != SASL_OK) 451 return ret; 452 453 if (!*result) { 454 PARAMERROR(utils); 455 return SASL_BADPARAM; 456 } 457 } 458 459 return ret; 460} 461 462/* 463 * Retrieve the client realm. 464 */ 465int _plug_get_realm(const sasl_utils_t *utils, const char **availrealms, 466 const char **realm, sasl_interact_t **prompt_need) 467{ 468 int ret; 469 sasl_getrealm_t *realm_cb; 470 void *realm_context; 471 sasl_interact_t *prompt; 472 473 /* see if we were given the result in the prompt */ 474 prompt = _plug_find_prompt(prompt_need, SASL_CB_GETREALM); 475 if (prompt != NULL) { 476 /* We prompted, and got.*/ 477 478 if (!prompt->result) { 479 SETERROR(utils, "Unexpectedly missing a prompt result"); 480 return SASL_BADPARAM; 481 } 482 483 *realm = prompt->result; 484 return SASL_OK; 485 } 486 487 /* Try to get the callback... */ 488 ret = utils->getcallback(utils->conn, SASL_CB_GETREALM, 489 &realm_cb, &realm_context); 490 491 if (ret == SASL_OK && realm_cb) { 492 ret = realm_cb(realm_context, SASL_CB_GETREALM, availrealms, realm); 493 if (ret != SASL_OK) 494 return ret; 495 496 if (!*realm) { 497 PARAMERROR(utils); 498 return SASL_BADPARAM; 499 } 500 } 501 502 return ret; 503} 504 505/* 506 * Make the requested prompts. (prompt==NULL means we don't want it) 507 */ 508int _plug_make_prompts(const sasl_utils_t *utils, 509 sasl_interact_t **prompts_res, 510 const char *user_prompt, const char *user_def, 511 const char *auth_prompt, const char *auth_def, 512 const char *pass_prompt, const char *pass_def, 513 const char *echo_chal, 514 const char *echo_prompt, const char *echo_def, 515 const char *realm_chal, 516 const char *realm_prompt, const char *realm_def) 517{ 518 int num = 1; 519 int alloc_size; 520 sasl_interact_t *prompts; 521 522 if (user_prompt) num++; 523 if (auth_prompt) num++; 524 if (pass_prompt) num++; 525 if (echo_prompt) num++; 526 if (realm_prompt) num++; 527 528 if (num == 1) { 529 SETERROR( utils, "make_prompts() called with no actual prompts" ); 530 return SASL_FAIL; 531 } 532 533 alloc_size = sizeof(sasl_interact_t)*num; 534 prompts = utils->malloc(alloc_size); 535 if (!prompts) { 536 MEMERROR( utils ); 537 return SASL_NOMEM; 538 } 539 memset(prompts, 0, alloc_size); 540 541 *prompts_res = prompts; 542 543 if (user_prompt) { 544 (prompts)->id = SASL_CB_USER; 545 (prompts)->challenge = "Authorization Name"; 546 (prompts)->prompt = user_prompt; 547 (prompts)->defresult = user_def; 548 549 prompts++; 550 } 551 552 if (auth_prompt) { 553 (prompts)->id = SASL_CB_AUTHNAME; 554 (prompts)->challenge = "Authentication Name"; 555 (prompts)->prompt = auth_prompt; 556 (prompts)->defresult = auth_def; 557 558 prompts++; 559 } 560 561 if (pass_prompt) { 562 (prompts)->id = SASL_CB_PASS; 563 (prompts)->challenge = "Password"; 564 (prompts)->prompt = pass_prompt; 565 (prompts)->defresult = pass_def; 566 567 prompts++; 568 } 569 570 if (echo_prompt) { 571 (prompts)->id = SASL_CB_ECHOPROMPT; 572 (prompts)->challenge = echo_chal; 573 (prompts)->prompt = echo_prompt; 574 (prompts)->defresult = echo_def; 575 576 prompts++; 577 } 578 579 if (realm_prompt) { 580 (prompts)->id = SASL_CB_GETREALM; 581 (prompts)->challenge = realm_chal; 582 (prompts)->prompt = realm_prompt; 583 (prompts)->defresult = realm_def; 584 585 prompts++; 586 } 587 588 /* add the ending one */ 589 (prompts)->id = SASL_CB_LIST_END; 590 (prompts)->challenge = NULL; 591 (prompts)->prompt = NULL; 592 (prompts)->defresult = NULL; 593 594 return SASL_OK; 595} 596 597/* 598 * Decode and concatenate multiple packets using the given function 599 * to decode each packet. 600 */ 601int _plug_decode(const sasl_utils_t *utils, 602 void *context, 603 const unsigned char *input, unsigned inputlen, 604 unsigned char **output, /* output buffer */ 605 unsigned int *outputsize, /* current size of output buffer */ 606 unsigned int *outputlen, /* length of data in output buffer */ 607 int (*decode_pkt)(void *context, 608 const unsigned char **input, unsigned int *inputlen, 609 unsigned char **output, unsigned int *outputlen)) 610{ 611 unsigned char *tmp = NULL; 612 unsigned tmplen = 0; 613 int ret; 614 615 *outputlen = 0; 616 617 while (inputlen!=0) 618 { 619 /* no need to free tmp */ 620 ret = decode_pkt(context, &input, &inputlen, &tmp, &tmplen); 621 622 if(ret != SASL_OK) return ret; 623 624 if (tmp!=NULL) /* if received 2 packets merge them together */ 625 { 626 ret = _plug_buf_alloc(utils, (unsigned char **)output, outputsize, *outputlen + tmplen + 1); 627 if(ret != SASL_OK) return ret; 628 629 memcpy(*output + *outputlen, tmp, tmplen); 630 631 /* Protect stupid clients */ 632 *(*output + *outputlen + tmplen) = '\0'; 633 634 *outputlen+=tmplen; 635 } 636 } 637 638 return SASL_OK; 639} 640 641/* returns the realm we should pretend to be in */ 642int _plug_parseuser(const sasl_utils_t *utils, 643 char **user, char **realm, const char *user_realm, 644 const char *serverFQDN, const char *input) 645{ 646 int ret; 647 char *r; 648 649 if(!user || !serverFQDN) { 650 PARAMERROR( utils ); 651 return SASL_BADPARAM; 652 } 653 654 r = strchr(input, '@'); 655 if (!r) { 656 /* hmmm, the user didn't specify a realm */ 657 if(user_realm && user_realm[0]) { 658 ret = _plug_strdup(utils, user_realm, realm, NULL); 659 } else { 660 /* Default to serverFQDN */ 661 ret = _plug_strdup(utils, serverFQDN, realm, NULL); 662 } 663 664 if (ret == SASL_OK) { 665 ret = _plug_strdup(utils, input, user, NULL); 666 } 667 } else { 668 r++; 669 ret = _plug_strdup(utils, r, realm, NULL); 670 *--r = '\0'; 671 *user = utils->malloc(r - input + 1); 672 if (*user) { 673 strncpy(*user, input, r - input +1); 674 } else { 675 MEMERROR( utils ); 676 ret = SASL_NOMEM; 677 } 678 *r = '@'; 679 } 680 681 return ret; 682} 683