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