1/* $Id: server.c,v 1.5 2005/05/17 21:56:45 snsimon 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#include <config.h> 43 44#include <stdio.h> 45#include <stdlib.h> 46#include <stdarg.h> 47#include <ctype.h> 48#include <errno.h> 49#include <string.h> 50 51#ifdef HAVE_UNISTD_H 52#include <unistd.h> 53#endif 54 55#include <sys/socket.h> 56#include <netinet/in.h> 57#include <arpa/inet.h> 58#include <netdb.h> 59 60#include <sasl.h> 61 62#include "common.h" 63 64#if !defined(IPV6_BINDV6ONLY) && defined(IN6P_IPV6_V6ONLY) 65#define IPV6_BINDV6ONLY IN6P_BINDV6ONLY 66#endif 67#if !defined(IPV6_V6ONLY) && defined(IPV6_BINDV6ONLY) 68#define IPV6_V6ONLY IPV6_BINDV6ONLY 69#endif 70#ifndef IPV6_BINDV6ONLY 71#undef IPV6_V6ONLY 72#endif 73 74/* create a socket listening on port 'port' */ 75/* if af is PF_UNSPEC more than one socket may be returned */ 76/* the returned list is dynamically allocated, so caller needs to free it */ 77int *listensock(const char *port, const int af) 78{ 79 struct addrinfo hints, *ai, *r; 80 int err, maxs, *sock, *socks; 81 const int on = 1; 82 83 memset(&hints, 0, sizeof(hints)); 84 hints.ai_flags = AI_PASSIVE; 85 hints.ai_family = af; 86 hints.ai_socktype = SOCK_STREAM; 87 err = getaddrinfo(NULL, port, &hints, &ai); 88 if (err) { 89 fprintf(stderr, "%s\n", gai_strerror(err)); 90 exit(EX_USAGE); 91 } 92 93 /* Count max number of sockets we may open */ 94 for (maxs = 0, r = ai; r; r = r->ai_next, maxs++) 95 ; 96 socks = malloc((maxs + 1) * sizeof(int)); 97 if (!socks) { 98 fprintf(stderr, "couldn't allocate memory for sockets\n"); 99 freeaddrinfo(ai); 100 exit(EX_OSERR); 101 } 102 103 socks[0] = 0; /* num of sockets counter at start of array */ 104 sock = socks + 1; 105 for (r = ai; r; r = r->ai_next) { 106 fprintf(stderr, "trying %d, %d, %d\n",r->ai_family, r->ai_socktype, r->ai_protocol); 107 *sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 108 if (*sock < 0) { 109 perror("socket"); 110 continue; 111 } 112 if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, 113 (void *) &on, sizeof(on)) < 0) { 114 perror("setsockopt(SO_REUSEADDR)"); 115 close(*sock); 116 continue; 117 } 118#if defined(IPV6_V6ONLY) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) 119 if (r->ai_family == AF_INET6) { 120 if (setsockopt(*sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, 121 (void *) &on, sizeof(on)) < 0) { 122 perror("setsockopt (IPV6_BINDV6ONLY)"); 123 close(*sock); 124 continue; 125 } 126 } 127#endif 128 if (bind(*sock, r->ai_addr, r->ai_addrlen) < 0) { 129 perror("bind"); 130 close(*sock); 131 continue; 132 } 133 134 if (listen(*sock, 5) < 0) { 135 perror("listen"); 136 close(*sock); 137 continue; 138 } 139 140 socks[0]++; 141 sock++; 142 } 143 144 freeaddrinfo(ai); 145 146 if (socks[0] == 0) { 147 fprintf(stderr, "Couldn't bind to any socket\n"); 148 free(socks); 149 exit(EX_OSERR); 150 } 151 152 return socks; 153} 154 155void usage(void) 156{ 157 fprintf(stderr, "usage: server [-p port] [-s service] [-m mech]\n"); 158 exit(EX_USAGE); 159} 160 161/* globals because i'm lazy */ 162char *mech; 163 164/* do the sasl negotiation; return -1 if it fails */ 165int mysasl_negotiate(FILE *in, FILE *out, sasl_conn_t *conn) 166{ 167 char buf[8192]; 168 char chosenmech[128]; 169 const char *data; 170 int len; 171 int r = SASL_FAIL; 172 const char *userid; 173 174 /* generate the capability list */ 175 if (mech) { 176 dprintf(2, "forcing use of mechanism %s\n", mech); 177 data = strdup(mech); 178 len = strlen(data); 179 } else { 180 int count; 181 182 dprintf(1, "generating client mechanism list... "); 183 r = sasl_listmech(conn, NULL, NULL, " ", NULL, 184 &data, &len, &count); 185 if (r != SASL_OK) saslfail(r, "generating mechanism list"); 186 dprintf(1, "%d mechanisms\n", count); 187 } 188 189 /* send capability list to client */ 190 send_string(out, data, len); 191 192 dprintf(1, "waiting for client mechanism...\n"); 193 len = recv_string(in, chosenmech, sizeof chosenmech); 194 if (len <= 0) { 195 printf("client didn't choose mechanism\n"); 196 fputc('N', out); /* send NO to client */ 197 fflush(out); 198 return -1; 199 } 200 201 if (mech && strcasecmp(mech, chosenmech)) { 202 printf("client didn't choose mandatory mechanism\n"); 203 fputc('N', out); /* send NO to client */ 204 fflush(out); 205 return -1; 206 } 207 208 len = recv_string(in, buf, sizeof(buf)); 209 if(len != 1) { 210 saslerr(r, "didn't receive first-send parameter correctly"); 211 fputc('N', out); 212 fflush(out); 213 return -1; 214 } 215 216 if(buf[0] == 'Y') { 217 /* receive initial response (if any) */ 218 len = recv_string(in, buf, sizeof(buf)); 219 220 /* start libsasl negotiation */ 221 r = sasl_server_start(conn, chosenmech, buf, len, 222 &data, &len); 223 } else { 224 r = sasl_server_start(conn, chosenmech, NULL, 0, 225 &data, &len); 226 } 227 228 if (r != SASL_OK && r != SASL_CONTINUE) { 229 saslerr(r, "starting SASL negotiation"); 230 fputc('N', out); /* send NO to client */ 231 fflush(out); 232 return -1; 233 } 234 235 while (r == SASL_CONTINUE) { 236 if (data) { 237 dprintf(2, "sending response length %d...\n", len); 238 fputc('C', out); /* send CONTINUE to client */ 239 send_string(out, data, len); 240 } else { 241 dprintf(2, "sending null response...\n"); 242 fputc('C', out); /* send CONTINUE to client */ 243 send_string(out, "", 0); 244 } 245 246 dprintf(1, "waiting for client reply...\n"); 247 len = recv_string(in, buf, sizeof buf); 248 if (len < 0) { 249 printf("client disconnected\n"); 250 return -1; 251 } 252 253 r = sasl_server_step(conn, buf, len, &data, &len); 254 if (r != SASL_OK && r != SASL_CONTINUE) { 255 saslerr(r, "performing SASL negotiation"); 256 fputc('N', out); /* send NO to client */ 257 fflush(out); 258 return -1; 259 } 260 } 261 262 if (r != SASL_OK) { 263 saslerr(r, "incorrect authentication"); 264 fputc('N', out); /* send NO to client */ 265 fflush(out); 266 return -1; 267 } 268 269 fputc('O', out); /* send OK to client */ 270 fflush(out); 271 dprintf(1, "negotiation complete\n"); 272 273 r = sasl_getprop(conn, SASL_USERNAME, (const void **) &userid); 274 printf("successful authentication '%s'\n", userid); 275 276 return 0; 277} 278 279int main(int argc, char *argv[]) 280{ 281 int c; 282 char *port = "12345"; 283 char *service = "rcmd"; 284 int *l, maxfd=0; 285 int r, i; 286 sasl_conn_t *conn; 287 288 while ((c = getopt(argc, argv, "p:s:m:")) != EOF) { 289 switch(c) { 290 case 'p': 291 port = optarg; 292 break; 293 294 case 's': 295 service = optarg; 296 break; 297 298 case 'm': 299 mech = optarg; 300 break; 301 302 default: 303 usage(); 304 break; 305 } 306 } 307 308 /* initialize the sasl library */ 309 r = sasl_server_init(NULL, "sample"); 310 if (r != SASL_OK) saslfail(r, "initializing libsasl"); 311 312 /* get a listening socket */ 313 if ((l = listensock(port, PF_UNSPEC)) == NULL) { 314 saslfail(SASL_FAIL, "allocating listensock"); 315 } 316 317 for (i = 1; i <= l[0]; i++) { 318 if (l[i] > maxfd) 319 maxfd = l[i]; 320 } 321 322 for (;;) { 323 char localaddr[NI_MAXHOST | NI_MAXSERV], 324 remoteaddr[NI_MAXHOST | NI_MAXSERV]; 325 char myhostname[1024+1]; 326 char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; 327 struct sockaddr_storage local_ip, remote_ip; 328 int niflags, error; 329 int salen; 330 int nfds, fd = -1; 331 FILE *in, *out; 332 fd_set readfds; 333 334 FD_ZERO(&readfds); 335 for (i = 1; i <= l[0]; i++) 336 FD_SET(l[i], &readfds); 337 338 nfds = select(maxfd + 1, &readfds, 0, 0, 0); 339 if (nfds <= 0) { 340 if (nfds < 0 && errno != EINTR) 341 perror("select"); 342 continue; 343 } 344 345 for (i = 1; i <= l[0]; i++) 346 if (FD_ISSET(l[i], &readfds)) { 347 fd = accept(l[i], NULL, NULL); 348 break; 349 } 350 351 if (fd < 0) { 352 if (errno != EINTR) 353 perror("accept"); 354 continue; 355 } 356 357 printf("accepted new connection\n"); 358 359 /* set ip addresses */ 360 salen = sizeof(local_ip); 361 if (getsockname(fd, (struct sockaddr *)&local_ip, &salen) < 0) { 362 perror("getsockname"); 363 } 364 niflags = (NI_NUMERICHOST | NI_NUMERICSERV); 365#ifdef NI_WITHSCOPEID 366 if (((struct sockaddr *)&local_ip)->sa_family == AF_INET6) 367 niflags |= NI_WITHSCOPEID; 368#endif 369 error = getnameinfo((struct sockaddr *)&local_ip, salen, hbuf, 370 sizeof(hbuf), pbuf, sizeof(pbuf), niflags); 371 if (error != 0) { 372 fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); 373 strcpy(hbuf, "unknown"); 374 strcpy(pbuf, "unknown"); 375 } 376 snprintf(localaddr, sizeof(localaddr), "%s;%s", hbuf, pbuf); 377 378 salen = sizeof(remote_ip); 379 if (getpeername(fd, (struct sockaddr *)&remote_ip, &salen) < 0) { 380 perror("getpeername"); 381 } 382 383 niflags = (NI_NUMERICHOST | NI_NUMERICSERV); 384#ifdef NI_WITHSCOPEID 385 if (((struct sockaddr *)&remote_ip)->sa_family == AF_INET6) 386 niflags |= NI_WITHSCOPEID; 387#endif 388 error = getnameinfo((struct sockaddr *)&remote_ip, salen, hbuf, 389 sizeof(hbuf), pbuf, sizeof(pbuf), niflags); 390 if (error != 0) { 391 fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); 392 strcpy(hbuf, "unknown"); 393 strcpy(pbuf, "unknown"); 394 } 395 snprintf(remoteaddr, sizeof(remoteaddr), "%s;%s", hbuf, pbuf); 396 397 r = gethostname(myhostname, sizeof(myhostname)-1); 398 if(r == -1) saslfail(r, "getting hostname"); 399 400 r = sasl_server_new(service, myhostname, NULL, localaddr, remoteaddr, 401 NULL, 0, &conn); 402 if (r != SASL_OK) saslfail(r, "allocating connection state"); 403 404 /* set external properties here 405 sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops); */ 406 407 /* set required security properties here 408 sasl_setprop(conn, SASL_SEC_PROPS, &secprops); */ 409 410 in = fdopen(fd, "r"); 411 out = fdopen(fd, "w"); 412 413 r = mysasl_negotiate(in, out, conn); 414 if (r == SASL_OK) { 415 /* send/receive data */ 416 417 418 } 419 420 printf("closing connection\n"); 421 fclose(in); 422 fclose(out); 423 close(fd); 424 sasl_dispose(&conn); 425 } 426 427 sasl_done(); 428} 429