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