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