1/* $Id: server.c,v 1.10 2010/12/01 14:51:53 mel Exp $ */ 2/* 3 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * 3. The name "Carnegie Mellon University" must not be used to 18 * endorse or promote products derived from this software without 19 * prior written permission. For permission or any other legal 20 * details, please contact 21 * Office of Technology Transfer 22 * Carnegie Mellon University 23 * 5000 Forbes Avenue 24 * Pittsburgh, PA 15213-3890 25 * (412) 268-4387, fax: (412) 268-7395 26 * tech-transfer@andrew.cmu.edu 27 * 28 * 4. Redistributions of any form whatsoever must retain the following 29 * acknowledgment: 30 * "This product includes software developed by Computing Services 31 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 32 * 33 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 34 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 35 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 36 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 37 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 38 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 39 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 40 */ 41/* 42 * Copyright 2009 by the Massachusetts Institute of Technology. 43 * All Rights Reserved. 44 * 45 * Export of this software from the United States of America may 46 * require a specific license from the United States Government. 47 * It is the responsibility of any person or organization contemplating 48 * export to obtain such a license before exporting. 49 * 50 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 51 * distribute this software and its documentation for any purpose and 52 * without fee is hereby granted, provided that the above copyright 53 * notice appear in all copies and that both that copyright notice and 54 * this permission notice appear in supporting documentation, and that 55 * the name of M.I.T. not be used in advertising or publicity pertaining 56 * to distribution of the software without specific, written prior 57 * permission. Furthermore if you modify this software you must label 58 * your software as modified software and not distribute it in such a 59 * fashion that it might be confused with the original M.I.T. software. 60 * M.I.T. makes no representations about the suitability of 61 * this software for any purpose. It is provided "as is" without express 62 * or implied warranty. 63 * 64 */ 65 66#include <stdio.h> 67#include <stdlib.h> 68#include <stdarg.h> 69#include <ctype.h> 70#include <errno.h> 71#include <string.h> 72#include <sysexits.h> 73#include <unistd.h> 74 75#ifdef HAVE_UNISTD_H 76#include <unistd.h> 77#endif 78 79#include <sys/socket.h> 80#include <netinet/in.h> 81#include <arpa/inet.h> 82#include <netdb.h> 83 84#include <sasl/sasl.h> 85 86#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE 87#include <gssapi/gssapi.h> 88#include <gssapi/gssapi_ext.h> 89#endif 90 91#include "common.h" 92 93#if !defined(IPV6_BINDV6ONLY) && defined(IN6P_IPV6_V6ONLY) 94#define IPV6_BINDV6ONLY IN6P_BINDV6ONLY 95#endif 96#if !defined(IPV6_V6ONLY) && defined(IPV6_BINDV6ONLY) 97#define IPV6_V6ONLY IPV6_BINDV6ONLY 98#endif 99#ifndef IPV6_BINDV6ONLY 100#undef IPV6_V6ONLY 101#endif 102 103#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE 104static OM_uint32 105enumerateAttributes(OM_uint32 *minor, 106 gss_name_t name, 107 int noisy); 108#endif 109 110/* create a socket listening on port 'port' */ 111/* if af is PF_UNSPEC more than one socket may be returned */ 112/* the returned list is dynamically allocated, so caller needs to free it */ 113int *listensock(const char *port, const int af) 114{ 115 struct addrinfo hints, *ai, *r; 116 int err, maxs, *sock, *socks; 117 const int on = 1; 118 119 memset(&hints, 0, sizeof(hints)); 120 hints.ai_flags = AI_PASSIVE; 121 hints.ai_family = af; 122 hints.ai_socktype = SOCK_STREAM; 123 err = getaddrinfo(NULL, port, &hints, &ai); 124 if (err) { 125 fprintf(stderr, "%s\n", gai_strerror(err)); 126 exit(EX_USAGE); 127 } 128 129 /* Count max number of sockets we may open */ 130 for (maxs = 0, r = ai; r; r = r->ai_next, maxs++) 131 ; 132 socks = malloc((maxs + 1) * sizeof(int)); 133 if (!socks) { 134 fprintf(stderr, "couldn't allocate memory for sockets\n"); 135 freeaddrinfo(ai); 136 exit(EX_OSERR); 137 } 138 139 socks[0] = 0; /* num of sockets counter at start of array */ 140 sock = socks + 1; 141 for (r = ai; r; r = r->ai_next) { 142 fprintf(stderr, "trying %d, %d, %d\n",r->ai_family, r->ai_socktype, r->ai_protocol); 143 *sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 144 if (*sock < 0) { 145 perror("socket"); 146 continue; 147 } 148 if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, 149 (void *) &on, sizeof(on)) < 0) { 150 perror("setsockopt(SO_REUSEADDR)"); 151 close(*sock); 152 continue; 153 } 154#if defined(IPV6_V6ONLY) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) 155 if (r->ai_family == AF_INET6) { 156 if (setsockopt(*sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, 157 (void *) &on, sizeof(on)) < 0) { 158 perror("setsockopt (IPV6_BINDV6ONLY)"); 159 close(*sock); 160 continue; 161 } 162 } 163#endif 164 if (bind(*sock, r->ai_addr, r->ai_addrlen) < 0) { 165 perror("bind"); 166 close(*sock); 167 continue; 168 } 169 170 if (listen(*sock, 5) < 0) { 171 perror("listen"); 172 close(*sock); 173 continue; 174 } 175 176 socks[0]++; 177 sock++; 178 } 179 180 freeaddrinfo(ai); 181 182 if (socks[0] == 0) { 183 fprintf(stderr, "Couldn't bind to any socket\n"); 184 free(socks); 185 exit(EX_OSERR); 186 } 187 188 return socks; 189} 190 191void usage(void) 192{ 193 fprintf(stderr, "usage: server [-C|-c] [-h hostname] [-p port] [-s service] [-m mech]\n"); 194 exit(EX_USAGE); 195} 196 197/* globals because i'm lazy */ 198char *mech; 199 200/* do the sasl negotiation; return -1 if it fails */ 201int mysasl_negotiate(FILE *in, FILE *out, sasl_conn_t *conn) 202{ 203 char buf[8192]; 204 char chosenmech[128]; 205 const char *data; 206 int len; 207 int r = SASL_FAIL; 208 const char *userid; 209#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE 210 gss_name_t peer = GSS_C_NO_NAME; 211#endif 212 213 /* generate the capability list */ 214 if (mech) { 215 dprintf(2, "forcing use of mechanism %s\n", mech); 216 data = strdup(mech); 217 len = strlen(data); 218 } else { 219 int count; 220 221 dprintf(1, "generating client mechanism list... "); 222 r = sasl_listmech(conn, NULL, NULL, " ", NULL, 223 &data, (unsigned int *)&len, &count); 224 if (r != SASL_OK) saslfail(r, "generating mechanism list"); 225 dprintf(1, "%d mechanisms\n", count); 226 } 227 228 /* send capability list to client */ 229 send_string(out, data, len); 230 231 dprintf(1, "waiting for client mechanism...\n"); 232 len = recv_string(in, chosenmech, sizeof chosenmech); 233 if (len <= 0) { 234 printf("client didn't choose mechanism\n"); 235 fputc('N', out); /* send NO to client */ 236 fflush(out); 237 return -1; 238 } 239 240 if (mech && strcasecmp(mech, chosenmech)) { 241 printf("client didn't choose mandatory mechanism\n"); 242 fputc('N', out); /* send NO to client */ 243 fflush(out); 244 return -1; 245 } 246 247 len = recv_string(in, buf, sizeof(buf)); 248 if(len != 1) { 249 saslerr(r, "didn't receive first-send parameter correctly"); 250 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 251 fputc('N', out); 252 fflush(out); 253 return -1; 254 } 255 256 if(buf[0] == 'Y') { 257 /* receive initial response (if any) */ 258 len = recv_string(in, buf, sizeof(buf)); 259 260 /* start libsasl negotiation */ 261 r = sasl_server_start(conn, chosenmech, buf, len, 262 &data, (unsigned int *)&len); 263 } else { 264 r = sasl_server_start(conn, chosenmech, NULL, 0, 265 &data, (unsigned int *)&len); 266 } 267 268 if (r != SASL_OK && r != SASL_CONTINUE) { 269 saslerr(r, "starting SASL negotiation"); 270 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 271 fputc('N', out); /* send NO to client */ 272 fflush(out); 273 return -1; 274 } 275 276 while (r == SASL_CONTINUE) { 277 if (data) { 278 dprintf(2, "sending response length %d...\n", len); 279 fputc('C', out); /* send CONTINUE to client */ 280 send_string(out, data, len); 281 } else { 282 dprintf(2, "sending null response...\n"); 283 fputc('C', out); /* send CONTINUE to client */ 284 send_string(out, "", 0); 285 } 286 287 dprintf(1, "waiting for client reply...\n"); 288 len = recv_string(in, buf, sizeof buf); 289 if (len < 0) { 290 printf("client disconnected\n"); 291 return -1; 292 } 293 294 r = sasl_server_step(conn, buf, len, &data, (unsigned int *)&len); 295 if (r != SASL_OK && r != SASL_CONTINUE) { 296 saslerr(r, "performing SASL negotiation"); 297 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 298 fputc('N', out); /* send NO to client */ 299 fflush(out); 300 return -1; 301 } 302 } 303 304 if (r != SASL_OK) { 305 saslerr(r, "incorrect authentication"); 306 fprintf(stderr, "%s\n", sasl_errdetail(conn)); 307 fputc('N', out); /* send NO to client */ 308 fflush(out); 309 return -1; 310 } 311 312 fputc('O', out); /* send OK to client */ 313 fflush(out); 314 dprintf(1, "negotiation complete\n"); 315 316 r = sasl_getprop(conn, SASL_USERNAME, (const void **) &userid); 317 printf("successful authentication '%s'\n", userid); 318 319#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE 320 r = sasl_getprop(conn, SASL_GSS_PEER_NAME, (const void **) &peer); 321 if (peer != GSS_C_NO_NAME) { 322 OM_uint32 minor; 323 enumerateAttributes(&minor, peer, 1); 324 } 325#endif 326 327 return 0; 328} 329 330int main(int argc, char *argv[]) 331{ 332 int c; 333 char *port = "12345"; 334 char *service = "rcmd"; 335 char *hostname = NULL; 336 int *l, maxfd=0; 337 int r, i; 338 sasl_conn_t *conn; 339 int cb_flag = 0; 340 341 while ((c = getopt(argc, argv, "Cch:p:s:m:")) != EOF) { 342 switch(c) { 343 case 'C': 344 cb_flag = 2; /* channel bindings are critical */ 345 break; 346 347 case 'c': 348 cb_flag = 1; /* channel bindings are present */ 349 break; 350 351 case 'h': 352 hostname = optarg; 353 break; 354 355 case 'p': 356 port = optarg; 357 break; 358 359 case 's': 360 service = optarg; 361 break; 362 363 case 'm': 364 mech = optarg; 365 break; 366 367 default: 368 usage(); 369 break; 370 } 371 } 372 373 /* initialize the sasl library */ 374 r = sasl_server_init(NULL, "sample"); 375 if (r != SASL_OK) saslfail(r, "initializing libsasl"); 376 377 /* get a listening socket */ 378 if ((l = listensock(port, PF_UNSPEC)) == NULL) { 379 saslfail(SASL_FAIL, "allocating listensock"); 380 } 381 382 for (i = 1; i <= l[0]; i++) { 383 if (l[i] > maxfd) 384 maxfd = l[i]; 385 } 386 387 for (;;) { 388 char localaddr[NI_MAXHOST | NI_MAXSERV], 389 remoteaddr[NI_MAXHOST | NI_MAXSERV]; 390 char myhostname[1024+1]; 391 char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; 392 struct sockaddr_storage local_ip, remote_ip; 393 int niflags, error; 394 socklen_t salen; 395 int nfds, fd = -1; 396 FILE *in, *out; 397 fd_set readfds; 398 sasl_channel_binding_t cb; 399 400 FD_ZERO(&readfds); 401 for (i = 1; i <= l[0]; i++) 402 FD_SET(l[i], &readfds); 403 404 nfds = select(maxfd + 1, &readfds, 0, 0, 0); 405 if (nfds <= 0) { 406 if (nfds < 0 && errno != EINTR) 407 perror("select"); 408 continue; 409 } 410 411 for (i = 1; i <= l[0]; i++) 412 if (FD_ISSET(l[i], &readfds)) { 413 fd = accept(l[i], NULL, NULL); 414 break; 415 } 416 417 if (fd < 0) { 418 if (errno != EINTR) 419 perror("accept"); 420 continue; 421 } 422 423 printf("accepted new connection\n"); 424 425 /* set ip addresses */ 426 salen = sizeof(local_ip); 427 if (getsockname(fd, (struct sockaddr *)&local_ip, &salen) < 0) { 428 perror("getsockname"); 429 } 430 niflags = (NI_NUMERICHOST | NI_NUMERICSERV); 431#ifdef NI_WITHSCOPEID 432 if (((struct sockaddr *)&local_ip)->sa_family == AF_INET6) 433 niflags |= NI_WITHSCOPEID; 434#endif 435 error = getnameinfo((struct sockaddr *)&local_ip, salen, hbuf, 436 sizeof(hbuf), pbuf, sizeof(pbuf), niflags); 437 if (error != 0) { 438 fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); 439 strcpy(hbuf, "unknown"); 440 strcpy(pbuf, "unknown"); 441 } 442 snprintf(localaddr, sizeof(localaddr), "%s;%s", hbuf, pbuf); 443 444 salen = sizeof(remote_ip); 445 if (getpeername(fd, (struct sockaddr *)&remote_ip, &salen) < 0) { 446 perror("getpeername"); 447 } 448 449 niflags = (NI_NUMERICHOST | NI_NUMERICSERV); 450#ifdef NI_WITHSCOPEID 451 if (((struct sockaddr *)&remote_ip)->sa_family == AF_INET6) 452 niflags |= NI_WITHSCOPEID; 453#endif 454 error = getnameinfo((struct sockaddr *)&remote_ip, salen, hbuf, 455 sizeof(hbuf), pbuf, sizeof(pbuf), niflags); 456 if (error != 0) { 457 fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); 458 strcpy(hbuf, "unknown"); 459 strcpy(pbuf, "unknown"); 460 } 461 snprintf(remoteaddr, sizeof(remoteaddr), "%s;%s", hbuf, pbuf); 462 463 if (hostname == NULL) { 464 r = gethostname(myhostname, sizeof(myhostname)-1); 465 if(r == -1) saslfail(r, "getting hostname"); 466 hostname = myhostname; 467 } 468 469 r = sasl_server_new(service, hostname, NULL, localaddr, remoteaddr, 470 NULL, 0, &conn); 471 if (r != SASL_OK) saslfail(r, "allocating connection state"); 472 473 cb.name = "sasl-sample"; 474 cb.critical = cb_flag > 1; 475 cb.data = "this is a test of channel binding"; 476 cb.len = strlen(cb.data); 477 478 if (cb_flag) { 479 sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb); 480 } 481 482 /* set external properties here 483 sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops); */ 484 485 /* set required security properties here 486 sasl_setprop(conn, SASL_SEC_PROPS, &secprops); */ 487 488 in = fdopen(fd, "r"); 489 out = fdopen(fd, "w"); 490 491 r = mysasl_negotiate(in, out, conn); 492 if (r == SASL_OK) { 493 /* send/receive data */ 494 495 496 } 497 498 printf("closing connection\n"); 499 fclose(in); 500 fclose(out); 501 close(fd); 502 sasl_dispose(&conn); 503 } 504 505 sasl_done(); 506} 507 508#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE 509static void displayStatus_1(m, code, type) 510 char *m; 511 OM_uint32 code; 512 int type; 513{ 514 OM_uint32 maj_stat, min_stat; 515 gss_buffer_desc msg; 516 OM_uint32 msg_ctx; 517 518 msg_ctx = 0; 519 while (1) { 520 maj_stat = gss_display_status(&min_stat, code, 521 type, GSS_C_NULL_OID, 522 &msg_ctx, &msg); 523 fprintf(stderr, "%s: %s\n", m, (char *)msg.value); 524 (void) gss_release_buffer(&min_stat, &msg); 525 526 if (!msg_ctx) 527 break; 528 } 529} 530 531static void displayStatus(msg, maj_stat, min_stat) 532 char *msg; 533 OM_uint32 maj_stat; 534 OM_uint32 min_stat; 535{ 536 displayStatus_1(msg, maj_stat, GSS_C_GSS_CODE); 537 displayStatus_1(msg, min_stat, GSS_C_MECH_CODE); 538} 539 540static void 541dumpAttribute(OM_uint32 *minor, 542 gss_name_t name, 543 gss_buffer_t attribute, 544 int noisy) 545{ 546 OM_uint32 major, tmp; 547 gss_buffer_desc value; 548 gss_buffer_desc display_value; 549 int authenticated = 0; 550 int complete = 0; 551 int more = -1; 552 unsigned int i; 553 554 while (more != 0) { 555 value.value = NULL; 556 display_value.value = NULL; 557 558 major = gss_get_name_attribute(minor, 559 name, 560 attribute, 561 &authenticated, 562 &complete, 563 &value, 564 &display_value, 565 &more); 566 if (GSS_ERROR(major)) { 567 displayStatus("gss_get_name_attribute", major, *minor); 568 break; 569 } 570 571 printf("Attribute %.*s %s %s\n\n%.*s\n", 572 (int)attribute->length, (char *)attribute->value, 573 authenticated ? "Authenticated" : "", 574 complete ? "Complete" : "", 575 (int)display_value.length, (char *)display_value.value); 576 577 if (noisy) { 578 for (i = 0; i < value.length; i++) { 579 if ((i % 32) == 0) 580 printf("\n"); 581 printf("%02x", ((char *)value.value)[i] & 0xFF); 582 } 583 printf("\n\n"); 584 } 585 586 gss_release_buffer(&tmp, &value); 587 gss_release_buffer(&tmp, &display_value); 588 } 589} 590 591static OM_uint32 592enumerateAttributes(OM_uint32 *minor, 593 gss_name_t name, 594 int noisy) 595{ 596 OM_uint32 major, tmp; 597 int name_is_MN; 598 gss_OID mech = GSS_C_NO_OID; 599 gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET; 600 unsigned int i; 601 602 major = gss_inquire_name(minor, 603 name, 604 &name_is_MN, 605 &mech, 606 &attrs); 607 if (GSS_ERROR(major)) { 608 displayStatus("gss_inquire_name", major, *minor); 609 return major; 610 } 611 612 if (attrs != GSS_C_NO_BUFFER_SET) { 613 for (i = 0; i < attrs->count; i++) 614 dumpAttribute(minor, name, &attrs->elements[i], noisy); 615 } 616 617 gss_release_oid(&tmp, &mech); 618 gss_release_buffer_set(&tmp, &attrs); 619 620 return major; 621} 622#endif 623