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