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