fec6355b585f5a68a4bc51559fb52c2af949105a
[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     int cb_flag = 0;
326
327     while ((c = getopt(argc, argv, "cp:s:m:")) != EOF) {
328         switch(c) {
329         case 'c':
330             cb_flag = 1;
331             break;
332
333         case 'p':
334             port = optarg;
335             break;
336
337         case 's':
338             service = optarg;
339             break;
340
341         case 'm':
342             mech = optarg;
343             break;
344
345         default:
346             usage();
347             break;
348         }
349     }
350
351     /* initialize the sasl library */
352     r = sasl_server_init(NULL, "sample");
353     if (r != SASL_OK) saslfail(r, "initializing libsasl");
354
355     /* get a listening socket */
356     if ((l = listensock(port, PF_UNSPEC)) == NULL) {
357         saslfail(SASL_FAIL, "allocating listensock");
358     }
359
360     for (i = 1; i <= l[0]; i++) {
361        if (l[i] > maxfd)
362            maxfd = l[i];
363     }
364
365     for (;;) {
366         char localaddr[NI_MAXHOST | NI_MAXSERV],
367              remoteaddr[NI_MAXHOST | NI_MAXSERV];
368         char myhostname[1024+1];
369         char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
370         struct sockaddr_storage local_ip, remote_ip;
371         int niflags, error;
372         int salen;
373         int nfds, fd = -1;
374         FILE *in, *out;
375         fd_set readfds;
376         sasl_channel_binding cb;
377
378         FD_ZERO(&readfds);
379         for (i = 1; i <= l[0]; i++)
380             FD_SET(l[i], &readfds);
381
382         nfds = select(maxfd + 1, &readfds, 0, 0, 0);
383         if (nfds <= 0) {
384             if (nfds < 0 && errno != EINTR)
385                 perror("select");
386             continue;
387         }
388
389        for (i = 1; i <= l[0]; i++) 
390            if (FD_ISSET(l[i], &readfds)) {
391                fd = accept(l[i], NULL, NULL);
392                break;
393            }
394
395         if (fd < 0) {
396             if (errno != EINTR)
397                 perror("accept");
398             continue;
399         }
400
401         printf("accepted new connection\n");
402
403         /* set ip addresses */
404         salen = sizeof(local_ip);
405         if (getsockname(fd, (struct sockaddr *)&local_ip, &salen) < 0) {
406             perror("getsockname");
407         }
408         niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
409 #ifdef NI_WITHSCOPEID
410         if (((struct sockaddr *)&local_ip)->sa_family == AF_INET6)
411             niflags |= NI_WITHSCOPEID;
412 #endif
413         error = getnameinfo((struct sockaddr *)&local_ip, salen, hbuf,
414                             sizeof(hbuf), pbuf, sizeof(pbuf), niflags);
415         if (error != 0) {
416             fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error));
417             strcpy(hbuf, "unknown");
418             strcpy(pbuf, "unknown");
419         }
420         snprintf(localaddr, sizeof(localaddr), "%s;%s", hbuf, pbuf);
421
422         salen = sizeof(remote_ip);
423         if (getpeername(fd, (struct sockaddr *)&remote_ip, &salen) < 0) {
424             perror("getpeername");
425         }
426
427         niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
428 #ifdef NI_WITHSCOPEID
429         if (((struct sockaddr *)&remote_ip)->sa_family == AF_INET6)
430             niflags |= NI_WITHSCOPEID;
431 #endif
432         error = getnameinfo((struct sockaddr *)&remote_ip, salen, hbuf,
433                             sizeof(hbuf), pbuf, sizeof(pbuf), niflags);
434         if (error != 0) {
435             fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error));
436             strcpy(hbuf, "unknown");
437             strcpy(pbuf, "unknown");
438         }
439         snprintf(remoteaddr, sizeof(remoteaddr), "%s;%s", hbuf, pbuf);
440
441         r = gethostname(myhostname, sizeof(myhostname)-1);
442         if(r == -1) saslfail(r, "getting hostname");
443
444         r = sasl_server_new(service, myhostname, NULL, localaddr, remoteaddr,
445                             NULL, 0, &conn);
446         if (r != SASL_OK) saslfail(r, "allocating connection state");
447
448         cb.type = "sasl-sample";
449         cb.critical = cb_flag;
450         cb.data = "this is a test of channel binding";
451         cb.len = strlen(cb.data);
452
453         sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb);
454
455         /* set external properties here
456            sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops); */
457
458         /* set required security properties here
459            sasl_setprop(conn, SASL_SEC_PROPS, &secprops); */
460
461         in = fdopen(fd, "r");
462         out = fdopen(fd, "w");
463
464         r = mysasl_negotiate(in, out, conn);
465         if (r == SASL_OK) {
466             /* send/receive data */
467
468
469         }
470
471         printf("closing connection\n");
472         fclose(in);
473         fclose(out);
474         close(fd);
475         sasl_dispose(&conn);
476     }
477
478     sasl_done();
479 }
480
481 static void displayStatus_1(m, code, type)
482     char *m;
483     OM_uint32 code;
484     int type;
485 {
486     OM_uint32 maj_stat, min_stat;
487     gss_buffer_desc msg;
488     OM_uint32 msg_ctx;
489
490     msg_ctx = 0;
491     while (1) {
492         maj_stat = gss_display_status(&min_stat, code,
493                                       type, GSS_C_NULL_OID,
494                                       &msg_ctx, &msg);
495         fprintf(stderr, "%s: %s\n", m, (char *)msg.value);
496         (void) gss_release_buffer(&min_stat, &msg);
497
498         if (!msg_ctx)
499             break;
500     }
501 }
502
503 static void displayStatus(msg, maj_stat, min_stat)
504     char *msg;
505     OM_uint32 maj_stat;
506     OM_uint32 min_stat;
507 {
508     displayStatus_1(msg, maj_stat, GSS_C_GSS_CODE);
509     displayStatus_1(msg, min_stat, GSS_C_MECH_CODE);
510 }
511
512 static void
513 dumpAttribute(OM_uint32 *minor,
514               gss_name_t name,
515               gss_buffer_t attribute,
516               int noisy)
517 {
518     OM_uint32 major, tmp;
519     gss_buffer_desc value;
520     gss_buffer_desc display_value;
521     int authenticated = 0;
522     int complete = 0;
523     int more = -1;
524     unsigned int i;
525
526     while (more != 0) {
527         value.value = NULL;
528         display_value.value = NULL;
529
530         major = gss_get_name_attribute(minor,
531                                        name,
532                                        attribute,
533                                        &authenticated,
534                                        &complete,
535                                        &value,
536                                        &display_value,
537                                        &more);
538         if (GSS_ERROR(major)) {
539             displayStatus("gss_get_name_attribute", major, *minor);
540             break;
541         }
542
543         printf("Attribute %.*s %s %s\n\n%.*s\n",
544                (int)attribute->length, (char *)attribute->value,
545                authenticated ? "Authenticated" : "",
546                complete ? "Complete" : "",
547                (int)display_value.length, (char *)display_value.value);
548
549         if (noisy) {
550             for (i = 0; i < value.length; i++) {
551                 if ((i % 32) == 0)
552                     printf("\n");
553                 printf("%02x", ((char *)value.value)[i] & 0xFF);
554             }
555             printf("\n\n");
556         }
557
558         gss_release_buffer(&tmp, &value);
559         gss_release_buffer(&tmp, &display_value);
560     }
561 }
562
563 static OM_uint32
564 enumerateAttributes(OM_uint32 *minor,
565                     gss_name_t name,
566                     int noisy)
567 {
568     OM_uint32 major, tmp;
569     int name_is_MN;
570     gss_OID mech = GSS_C_NO_OID;
571     gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
572     unsigned int i;
573
574     major = gss_inquire_name(minor,
575                              name,
576                              &name_is_MN,
577                              &mech,
578                              &attrs);
579     if (GSS_ERROR(major)) {
580         displayStatus("gss_inquire_name", major, *minor);
581         return major;
582     }
583
584     if (attrs != GSS_C_NO_BUFFER_SET) {
585         for (i = 0; i < attrs->count; i++)
586             dumpAttribute(minor, name, &attrs->elements[i], noisy);
587     }
588
589     gss_release_oid(&tmp, &mech);
590     gss_release_buffer_set(&tmp, &attrs);
591
592     return major;
593 }