1/* sample-server.c -- sample SASL server 2 * Rob Earhart 3 * $Id: sample-server.c,v 1.34 2011/09/01 14:12:18 mel Exp $ 4 */ 5/* 6 * Copyright (c) 1998-2003 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 "../../cyrus_sasl/config.h" 46#include <limits.h> 47#include <stdio.h> 48 49#ifdef HAVE_GETOPT_H 50#include <getopt.h> 51#endif 52#ifdef HAVE_UNISTD_H 53#include <unistd.h> 54#endif 55 56#ifdef WIN32 57# include <winsock2.h> 58__declspec(dllimport) char *optarg; 59__declspec(dllimport) int optind; 60__declspec(dllimport) int getsubopt(char **optionp, const char * const *tokens, char **valuep); 61#define HAVE_GETSUBOPT 62#else /* WIN32 */ 63# include <netinet/in.h> 64#endif /* WIN32 */ 65#ifdef __APPLE__ 66#include <sasl/sasl.h> 67#include <sasl/saslplug.h> 68#include <sasl/saslutil.h> 69#else 70#include <sasl.h> 71#include <saslplug.h> 72#include <saslutil.h> 73#endif 74 75#ifndef HAVE_GETSUBOPT 76int getsubopt(char **optionp, const char * const *tokens, char **valuep); 77#endif 78 79static const char 80build_ident[] = "$Build: sample-server " PACKAGE "-" VERSION " $"; 81 82static const char *progname = NULL; 83static int verbose; 84 85/* Note: if this is changed, change it in samp_read(), too. */ 86#define SAMPLE_SEC_BUF_SIZE (2048) 87 88static const char 89message[] = "Come here Watson, I want you."; 90 91char buf[SAMPLE_SEC_BUF_SIZE]; 92 93static const char *bit_subopts[] = { 94#define OPT_MIN (0) 95 "min", 96#define OPT_MAX (1) 97 "max", 98 NULL 99}; 100 101static const char *ext_subopts[] = { 102#define OPT_EXT_SSF (0) 103 "ssf", 104#define OPT_EXT_ID (1) 105 "id", 106 NULL 107}; 108 109static const char *flag_subopts[] = { 110#define OPT_NOPLAIN (0) 111 "noplain", 112#define OPT_NOACTIVE (1) 113 "noactive", 114#define OPT_NODICT (2) 115 "nodict", 116#define OPT_FORWARDSEC (3) 117 "forwardsec", 118#define OPT_NOANONYMOUS (4) 119 "noanonymous", 120#define OPT_PASSCRED (5) 121 "passcred", 122 NULL 123}; 124 125static const char *ip_subopts[] = { 126#define OPT_IP_LOCAL (0) 127 "local", 128#define OPT_IP_REMOTE (1) 129 "remote", 130 NULL 131}; 132 133char *mech = NULL, 134 *iplocal = NULL, 135 *ipremote = NULL, 136 *searchpath = NULL, 137 *service = "rcmd", 138 *localdomain = NULL, 139 *userdomain = NULL; 140sasl_conn_t *conn = NULL; 141 142static void 143free_conn(void) 144{ 145 if (conn) 146 sasl_dispose(&conn); 147} 148 149static int 150sasl_my_log(void *context __attribute__((unused)), 151 int priority, 152 const char *message) 153{ 154 const char *label; 155 156 if (! message) 157 return SASL_BADPARAM; 158 159 switch (priority) { 160 case SASL_LOG_ERR: 161 label = "Error"; 162 break; 163 case SASL_LOG_NOTE: 164 label = "Info"; 165 break; 166 default: 167 label = "Other"; 168 break; 169 } 170 171 fprintf(stderr, "%s: SASL %s: %s\n", 172 progname, label, message); 173 174 return SASL_OK; 175} 176 177static int 178getpath(void *context __attribute__((unused)), 179 char ** path) 180{ 181 if (! path) 182 return SASL_BADPARAM; 183 184 if (searchpath) { 185 *path = searchpath; 186 } else { 187 *path = PLUGINDIR; 188 } 189 190 return SASL_OK; 191} 192 193static sasl_callback_t callbacks[] = { 194 { 195 SASL_CB_LOG, (sasl_callback_ft)&sasl_my_log, NULL 196 }, { 197 SASL_CB_GETPATH, (sasl_callback_ft)&getpath, NULL 198 }, { 199 SASL_CB_LIST_END, NULL, NULL 200 } 201}; 202 203static void 204sasldebug(int why, const char *what, const char *errstr) 205{ 206 fprintf(stderr, "%s: %s: %s", 207 progname, 208 what, 209 sasl_errstring(why, NULL, NULL)); 210 if (errstr) 211 fprintf(stderr, " (%s)\n", errstr); 212 else 213 putc('\n', stderr); 214} 215 216static void 217saslfail(int why, const char *what, const char *errstr) 218{ 219 sasldebug(why, what, errstr); 220 exit(EXIT_FAILURE); 221} 222 223static void 224fail(const char *what) 225{ 226 fprintf(stderr, "%s: %s\n", 227 progname, what); 228 exit(EXIT_FAILURE); 229} 230 231static void 232osfail() 233{ 234 perror(progname); 235 exit(EXIT_FAILURE); 236} 237 238static void 239samp_send(const char *buffer, 240 unsigned length) 241{ 242 char *buf; 243 unsigned len, alloclen; 244 int result; 245 246 alloclen = ((length / 3) + 1) * 4 + 1; 247 buf = malloc(alloclen); 248 if (! buf) 249 osfail(); 250 251 result = sasl_encode64(buffer, length, buf, alloclen, &len); 252 if (result != SASL_OK) 253 saslfail(result, "Encoding data in base64", NULL); 254 printf("S: %s\n", buf); 255 free(buf); 256 fflush(stdout); 257} 258 259static unsigned 260samp_recv() 261{ 262 unsigned len; 263 int result; 264 265 if (! fgets(buf, SAMPLE_SEC_BUF_SIZE, stdin)) { 266 fail("Unable to parse input"); 267 } 268 269 if (strncmp(buf, "C: ", 3) != 0) { 270 fail("Line must start with 'C: '"); 271 } 272 273 len = strlen(buf); 274 if (len > 0 && buf[len-1] == '\n') { 275 buf[len-1] = '\0'; 276 } 277 278 result = sasl_decode64(buf + 3, (unsigned) strlen(buf + 3), buf, 279 SAMPLE_SEC_BUF_SIZE, &len); 280 if (result != SASL_OK) 281 saslfail(result, "Decoding data from base64", NULL); 282 buf[len] = '\0'; 283 printf("got '%s'\n", buf); 284 return len; 285} 286 287 288int 289main(int argc, char *argv[]) 290{ 291 int c = 0; 292 int errflag = 0; 293 int result; 294 sasl_security_properties_t secprops; 295 sasl_ssf_t extssf = 0; 296 const char *ext_authid = NULL; 297 char *options, *value; 298 unsigned len, count; 299 const char *data; 300 int serverlast = 0; 301 sasl_ssf_t *ssf; 302 303#ifdef WIN32 304 /* initialize winsock */ 305 WSADATA wsaData; 306 307 result = WSAStartup( MAKEWORD(2, 0), &wsaData ); 308 if ( result != 0) { 309 saslfail(SASL_FAIL, "Initializing WinSockets", NULL); 310 } 311#endif 312 313 progname = strrchr(argv[0], HIER_DELIMITER); 314 if (progname) 315 progname++; 316 else 317 progname = argv[0]; 318 319 /* Init defaults... */ 320 memset(&secprops, 0L, sizeof(secprops)); 321 secprops.maxbufsize = SAMPLE_SEC_BUF_SIZE; 322 secprops.max_ssf = UINT_MAX; 323 324 verbose = 0; 325 while ((c = getopt(argc, argv, "vlhb:e:m:f:i:p:s:d:u:?")) != EOF) 326 switch (c) { 327 case 'v': 328 verbose = 1; 329 break; 330 case 'b': 331 options = optarg; 332 while (*options != '\0') 333 switch(getsubopt(&options, (char * const *)bit_subopts, &value)) { 334 case OPT_MIN: 335 if (! value) 336 errflag = 1; 337 else 338 secprops.min_ssf = atoi(value); 339 break; 340 case OPT_MAX: 341 if (! value) 342 errflag = 1; 343 else 344 secprops.max_ssf = atoi(value); 345 break; 346 default: 347 errflag = 1; 348 break; 349 } 350 break; 351 352 case 'e': 353 options = optarg; 354 while (*options != '\0') 355 switch(getsubopt(&options, (char * const *)ext_subopts, &value)) { 356 case OPT_EXT_SSF: 357 if (! value) 358 errflag = 1; 359 else 360 extssf = atoi(value); 361 break; 362 case OPT_MAX: 363 if (! value) 364 errflag = 1; 365 else 366 ext_authid = value; 367 break; 368 default: 369 errflag = 1; 370 break; 371 } 372 break; 373 374 case 'm': 375 mech = optarg; 376 break; 377 378 case 'f': 379 options = optarg; 380 while (*options != '\0') { 381 switch(getsubopt(&options, (char * const *)flag_subopts, &value)) { 382 case OPT_NOPLAIN: 383 secprops.security_flags |= SASL_SEC_NOPLAINTEXT; 384 break; 385 case OPT_NOACTIVE: 386 secprops.security_flags |= SASL_SEC_NOACTIVE; 387 break; 388 case OPT_NODICT: 389 secprops.security_flags |= SASL_SEC_NODICTIONARY; 390 break; 391 case OPT_FORWARDSEC: 392 secprops.security_flags |= SASL_SEC_FORWARD_SECRECY; 393 break; 394 case OPT_NOANONYMOUS: 395 secprops.security_flags |= SASL_SEC_NOANONYMOUS; 396 break; 397 case OPT_PASSCRED: 398 secprops.security_flags |= SASL_SEC_PASS_CREDENTIALS; 399 break; 400 default: 401 errflag = 1; 402 break; 403 } 404 if (value) errflag = 1; 405 } 406 break; 407 408 case 'l': 409 serverlast = SASL_SUCCESS_DATA; 410 break; 411 412 case 'i': 413 options = optarg; 414 while (*options != '\0') 415 switch(getsubopt(&options, (char * const *)ip_subopts, &value)) { 416 case OPT_IP_LOCAL: 417 if (! value) 418 errflag = 1; 419 else 420 iplocal = value; 421 break; 422 case OPT_IP_REMOTE: 423 if (! value) 424 errflag = 1; 425 else 426 ipremote = value; 427 break; 428 default: 429 errflag = 1; 430 break; 431 } 432 break; 433 434 case 'p': 435 searchpath = optarg; 436 break; 437 438 case 's': 439 service = optarg; 440 break; 441 442 case 'd': 443 localdomain = optarg; 444 break; 445 446 case 'u': 447 userdomain = optarg; 448 break; 449 450 default: 451 errflag = 1; 452 break; 453 } 454 455 if (optind != argc) { 456 457 errflag = 1; 458 } 459 460 if (errflag) { 461 fprintf(stderr, "%s: Usage: %s [-b min=N,max=N] [-e ssf=N,id=ID] [-m MECH] [-f FLAGS] [-i local=IP,remote=IP] [-p PATH] [-d DOM] [-u DOM] [-s NAME]\n" 462 "\t-b ...\t#bits to use for encryption\n" 463 "\t\tmin=N\tminumum #bits to use (1 => integrity)\n" 464 "\t\tmax=N\tmaximum #bits to use\n" 465 "\t-e ...\tassume external encryption\n" 466 "\t\tssf=N\texternal mech provides N bits of encryption\n" 467 "\t\tid=ID\texternal mech provides authentication id ID\n" 468 "\t-m MECH\tforce use of MECH for security\n" 469 "\t-f ...\tset security flags\n" 470 "\t\tnoplain\t\trequire security vs. passive attacks\n" 471 "\t\tnoactive\trequire security vs. active attacks\n" 472 "\t\tnodict\t\trequire security vs. passive dictionary attacks\n" 473 "\t\tforwardsec\trequire forward secrecy\n" 474 "\t\tmaximum\t\trequire all security flags\n" 475 "\t\tpasscred\tattempt to receive client credentials\n" 476 "\t-i ...\tset IP addresses (required by some mechs)\n" 477 "\t\tlocal=IP;PORT\tset local address to IP, port PORT\n" 478 "\t\tremote=IP;PORT\tset remote address to IP, port PORT\n" 479 "\t-p PATH\tcolon-seperated search path for mechanisms\n" 480 "\t-s NAME\tservice name to pass to mechanisms\n" 481 "\t-d DOM\tlocal server domain\n" 482 "\t-u DOM\tuser domain\n" 483 "\t-l\tenable server-send-last\n", 484 progname, progname); 485 exit(EXIT_FAILURE); 486 } 487 488 result = sasl_server_init(callbacks, "sample"); 489 if (result != SASL_OK) { 490 saslfail(result, "Initializing libsasl", NULL); 491 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 492 } 493 494 atexit(&sasl_done); 495 496 result = sasl_server_new(service, 497 localdomain, 498 userdomain, 499 iplocal, 500 ipremote, 501 NULL, 502 serverlast, 503 &conn); 504 if (result != SASL_OK) { 505 saslfail(result, "Allocating sasl connection state", NULL); 506 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 507 } 508 509 atexit(&free_conn); 510 511 if(extssf) { 512 result = sasl_setprop(conn, 513 SASL_SSF_EXTERNAL, 514 &extssf); 515 516 if (result != SASL_OK) { 517 saslfail(result, "Setting external SSF", NULL); 518 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 519 } 520 } 521 522 if(ext_authid) { 523 result = sasl_setprop(conn, 524 SASL_AUTH_EXTERNAL, 525 &ext_authid); 526 527 if (result != SASL_OK) { 528 saslfail(result, "Setting external authid", NULL); 529 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 530 } 531 } 532 533 result = sasl_setprop(conn, 534 SASL_SEC_PROPS, 535 &secprops); 536 537 if (result != SASL_OK) { 538 saslfail(result, "Setting security properties", NULL); 539 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 540 } 541 542 if (mech) { 543 printf("Forcing use of mechanism %s\n", mech); 544 data = strdup(mech); 545 if (! data) 546 osfail(); 547 len = (unsigned) strlen(data); 548 count = 1; 549 } else { 550 puts("Generating client mechanism list..."); 551 result = sasl_listmech(conn, 552 ext_authid, 553 NULL, 554 " ", 555 NULL, 556 &data, 557 &len, 558 (int *)&count); 559 if (result != SASL_OK) { 560 saslfail(result, "Generating client mechanism list", NULL); 561 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 562 } 563 } 564 565 printf("Sending list of %d mechanism(s)\n", count); 566 samp_send(data, len); 567 568 if(mech) { 569 free((void *)data); 570 } 571 572 puts("Waiting for client mechanism..."); 573 len = samp_recv(); 574 if (mech && strcasecmp(mech, buf)) 575 fail("Client chose something other than the mandatory mechanism"); 576 if (strlen(buf) < len) { 577 /* Hmm, there's an initial response here */ 578 data = buf + strlen(buf) + 1; 579 len = len - (unsigned) strlen(buf) - 1; 580 } else { 581 data = NULL; 582 len = 0; 583 } 584 result = sasl_server_start(conn, 585 buf, 586 data, 587 len, 588 &data, 589 &len); 590 if (result != SASL_OK && result != SASL_CONTINUE) { 591 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 592 saslfail(result, "Starting SASL negotiation", sasl_errstring(result,NULL,NULL)); 593 } 594 595 while (result == SASL_CONTINUE) { 596 if (data) { 597 puts("Sending response..."); 598 samp_send(data, len); 599 } else 600 fail("No data to send--something's wrong"); 601 puts("Waiting for client reply..."); 602 len = samp_recv(); 603 data = NULL; 604 result = sasl_server_step(conn, buf, len, 605 &data, &len); 606 if (result != SASL_OK && result != SASL_CONTINUE) { 607 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 608 saslfail(result, "Performing SASL negotiation", sasl_errstring(result,NULL,NULL)); 609 } 610 } 611 puts("Negotiation complete"); 612 613 if(serverlast&&data) { 614 printf("might need additional send:\n"); 615 samp_send(data,len); 616 } 617 618 result = sasl_getprop(conn, SASL_USERNAME, (const void **)&data); 619 if (result != SASL_OK) 620 sasldebug(result, "username", NULL); 621 else 622 printf("Username: %s\n", data ? data : "(NULL)"); 623 624 result = sasl_getprop(conn, SASL_DEFUSERREALM, (const void **)&data); 625 if (result != SASL_OK) 626 sasldebug(result, "realm", NULL); 627 else 628 printf("Realm: %s\n", data ? data : "(NULL)"); 629 630 result = sasl_getprop(conn, SASL_SSF, (const void **)&ssf); 631 if (result != SASL_OK) 632 sasldebug(result, "ssf", NULL); 633 else 634 printf("SSF: %d\n", *ssf); 635#define CLIENT_MSG1 "client message 1" 636#define SERVER_MSG1 "srv message 1" 637 result=sasl_encode(conn,SERVER_MSG1,sizeof(SERVER_MSG1), 638 &data,&len); 639 if (result != SASL_OK) { 640 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 641 saslfail(result, "sasl_encode", NULL); 642 } 643 printf("sending encrypted message '%s'\n",SERVER_MSG1); 644 samp_send(data,len); 645 printf("Waiting for encrypted message...\n"); 646 len=samp_recv(); 647 { 648 unsigned int recv_len; 649 const char *recv_data; 650 result=sasl_decode(conn,buf,len,&recv_data,&recv_len); 651 if (result != SASL_OK) { 652 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 653 saslfail(result, "sasl_encode", NULL); 654 } 655 printf("recieved decoded message '%s'\n",recv_data); 656 if(strcmp(recv_data,CLIENT_MSG1)!=0) { 657 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 658 saslfail(1,"recive decoded server message",NULL); 659 } 660 } 661 662#ifdef WIN32 663 WSACleanup(); 664#endif 665 666 return (EXIT_SUCCESS); 667} 668