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