1 /* $Header: /var/cvs/mbdyn/mbdyn/mbdyn-1.0/libraries/libmbutil/mbsasl.c,v 1.20 2017/01/12 14:44:05 masarati Exp $ */
2 /*
3  * MBDyn (C) is a multibody analysis code.
4  * http://www.mbdyn.org
5  *
6  * Copyright (C) 1996-2017
7  *
8  * Pierangelo Masarati	<masarati@aero.polimi.it>
9  * Paolo Mantegazza	<mantegazza@aero.polimi.it>
10  *
11  * Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
12  * via La Masa, 34 - 20156 Milano, Italy
13  * http://www.aero.polimi.it
14  *
15  * Changing this copyright notice is forbidden.
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation (version 2 of the License).
20  *
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30  */
31 
32 /*
33  * The protocol is very simple.  It is client-initiated (of course),
34  * by sending
35  *
36  * C: M
37  * S: M<length><mechanism list>
38  * C: S<length><mechanism chosen><length><additional data>
39  *
40  * S: 	C<length><additional data>
41  * C: 	C<length><additional data>
42  *
43  * S: O | F
44  *
45  * where M means "Methods", C means "Continuation" (with data),
46  * O means "OK" and F means "Fail"
47  */
48 
49 #include "mbconfig.h"           /* This goes first in every *.c,*.cc file */
50 
51 #ifdef HAVE_SASL2
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <errno.h>
57 #include <sys/mman.h>
58 #include <fcntl.h>
59 #include <signal.h>
60 #include <netdb.h>
61 #include <netinet/in.h>
62 #include <sys/types.h>
63 #include <sys/poll.h>
64 #include <sys/socket.h>
65 
66 #include <sasl/sasl.h>
67 #include "mbsasl.h"
68 
69 /* global vars that contain the auth data */
70 static struct mbdyn_sasl_t	global_mbdyn_sasl = MBDYN_SASL_INIT;
71 
72 static int
get_secret_default_f(sasl_conn_t * conn,void * context,int id,sasl_secret_t ** psecret)73 get_secret_default_f(sasl_conn_t *conn,
74 		void *context,
75 		int id,
76 		sasl_secret_t **psecret)
77 {
78 	size_t		credlen;
79 	const char	*cred = global_mbdyn_sasl.sasl_cred;
80 
81 	if (id != SASL_CB_PASS) return SASL_FAIL;
82 
83 	if (cred == NULL) {
84 		cred = getpass("password: ");
85 	}
86 
87 	credlen = strlen(cred);
88 	*psecret = malloc(sizeof(sasl_secret_t) + credlen);
89 	(*psecret)->len = credlen;
90 	memcpy(&(*psecret)->data[0], cred, credlen);
91 
92 	return SASL_OK;
93 }
94 
95 static int
get_authname_default_f(void * context,int id,const char ** result,unsigned * len)96 get_authname_default_f(void *context,
97 		int id,
98 		const char **result,
99 		unsigned *len)
100 {
101 	const char	*authz = global_mbdyn_sasl.sasl_authz;
102 	char		buf[MBDYN_SASL_BUFSIZE];
103 
104 	if (id != SASL_CB_AUTHNAME) return SASL_FAIL;
105 
106 	if (!authz && (global_mbdyn_sasl.sasl_flags & MBDYN_SASL_FLAG_USERAUTHZ)) {
107 		authz = global_mbdyn_sasl.sasl_user;
108 	}
109 
110 	if (!authz) {
111 		size_t	buflen;
112 
113 		printf("authzname: ");
114 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
115 			return SASL_FAIL;
116 		}
117 
118 		buflen = strlen(buf);
119 		if (buf[buflen - 1] == '\n') {
120 			--buflen;
121 			buf[buflen] = '\0';
122 		}
123 
124 		if (buflen > 0) {
125 			authz = buf;
126 		}
127 	}
128 
129 	if (authz) {
130 		*result = strdup(authz);
131 		if (len) {
132 			*len = strlen(*result);
133 		}
134 	} else {
135 		*result = NULL;
136 		if (len) {
137 			*len = 0;
138 		}
139 	}
140 
141 	return SASL_OK;
142 }
143 
144 static int
get_user_default_f(void * context,int id,const char ** result,unsigned * len)145 get_user_default_f(void *context,
146 		int id,
147 		const char **result,
148 		unsigned *len)
149 {
150 	const char	*user = global_mbdyn_sasl.sasl_user;
151 	char		buf[MBDYN_SASL_BUFSIZE];
152 
153 	if (id != SASL_CB_USER) return SASL_FAIL;
154 
155 	if (!user) {
156 		size_t	buflen;
157 
158 		printf("username: ");
159 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
160 			return SASL_FAIL;
161 		}
162 
163 		buflen = strlen(buf);
164 		if (buf[buflen - 1] == '\n') {
165 			--buflen;
166 			buf[buflen] = '\0';
167 		}
168 		user = buf;
169 	}
170 
171 	*result = strdup(user);
172 	if (len) {
173 		*len = strlen(*result);
174 	}
175 
176 	return SASL_OK;
177 }
178 
179 static int
get_realm_default_f(void * context,int id,const char ** availrealms,const char ** result)180 get_realm_default_f(void *context,
181 		int id,
182 		const char **availrealms,
183 		const char **result)
184 {
185 	const char	*realm = global_mbdyn_sasl.sasl_realm;
186 	char		buf[MBDYN_SASL_BUFSIZE];
187 
188 	if (id != SASL_CB_GETREALM) return SASL_FAIL;
189 
190 	if (!realm) {
191 		size_t	buflen;
192 
193 		printf("realm: ");
194 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
195 			return SASL_FAIL;
196 		}
197 
198 		buflen = strlen(buf);
199 		if (buf[buflen - 1] == '\n') {
200 			--buflen;
201 			buf[buflen] = '\0';
202 		}
203 
204 		if (buflen > 0) {
205 			realm = buf;
206 		}
207 	}
208 
209 	if (realm) {
210 		*result = strdup(realm);
211 	} else {
212 		*result = NULL;
213 	}
214 
215 	return SASL_OK;
216 }
217 
218 static int
log_server_default_f(void * context,int level,const char * message)219 log_server_default_f(void *context,
220 		int level,
221 		const char *message)
222 {
223 	fprintf(stderr, "[server %d] %s\n", level, message);
224 	return 0;
225 }
226 
227 static int
log_client_default_f(void * context,int level,const char * message)228 log_client_default_f(void *context,
229 		int level,
230 		const char *message)
231 {
232 	fprintf(stderr, "[client %d] %s\n", level, message);
233 	return 0;
234 }
235 
236 sasl_log_t		*log_server_f = &log_server_default_f;
237 sasl_log_t		*log_client_f = &log_client_default_f;
238 sasl_getsimple_t	*get_user_f = &get_user_default_f;
239 sasl_getsimple_t	*get_authname_f = &get_authname_default_f;
240 sasl_getsecret_t	*get_secret_f = &get_secret_default_f;
241 sasl_getrealm_t		*get_realm_f = &get_realm_default_f;
242 
243 static sasl_callback_t server_callbacks[] = {
244 	{ SASL_CB_LOG, 		NULL /* log_server_f */ ,	NULL },
245 	{ SASL_CB_LIST_END,	NULL,				NULL }
246 };
247 
248 static sasl_callback_t client_callbacks[] = {
249 	{ SASL_CB_GETREALM,	NULL /* get_realm_f */ ,	NULL },
250 	{ SASL_CB_USER, 	NULL /* get_user_f */ ,		NULL },
251 	{ SASL_CB_AUTHNAME, 	NULL /* get_authname_f */ ,	NULL },
252 	{ SASL_CB_PASS,		NULL /* get_secret_f */ ,	NULL },
253 	{ SASL_CB_LOG,		NULL /* log_client_f */ ,	NULL },
254 	{ SASL_CB_LIST_END,	NULL,				NULL }
255 };
256 
257 int
mbdyn_sasl_client_init(struct mbdyn_sasl_t * mbdyn_sasl)258 mbdyn_sasl_client_init(struct mbdyn_sasl_t *mbdyn_sasl)
259 {
260 	int	rc;
261 
262 	client_callbacks[0].proc = get_realm_f;
263 	client_callbacks[1].proc = get_user_f;
264 	client_callbacks[2].proc = get_authname_f;
265 	client_callbacks[3].proc = get_secret_f;
266 	client_callbacks[4].proc = log_client_f;
267 
268 	if (mbdyn_sasl) {
269 		global_mbdyn_sasl = *mbdyn_sasl;
270 	}
271 
272 	rc = sasl_client_init(client_callbacks); /* Callbacks supported */
273 	if (rc != SASL_OK) {
274 		printf("[client] sasl_client_init() failed: %d (%s)\n",
275 				rc, sasl_errstring(rc, NULL, NULL));
276 	}
277 
278 	return rc;
279 }
280 
281 int
mbdyn_sasl_server_init(struct mbdyn_sasl_t * mbdyn_sasl)282 mbdyn_sasl_server_init(struct mbdyn_sasl_t *mbdyn_sasl)
283 {
284 	int	rc;
285 
286 	server_callbacks[0].proc = log_server_f;
287 
288 	rc = sasl_server_init(server_callbacks,	/* Callbacks supported */
289 			MBDYN_SASL_CONFFILE);	/* Name of the application,
290 						 * used to look for rtai.conf
291 						 * file in /usr/lib/sasl2/ */
292 	if (rc != SASL_OK) {
293 		printf("[server] sasl_server_init() failed: %d (%s)\n",
294 				rc, sasl_errstring(rc, NULL, NULL));
295 	}
296 
297 	return rc;
298 }
299 
300 int
mbdyn_sasl_init(struct mbdyn_sasl_t * mbdyn_sasl)301 mbdyn_sasl_init(struct mbdyn_sasl_t *mbdyn_sasl)
302 {
303 	switch (mbdyn_sasl->use_sasl) {
304 	case MBDYN_SASL_SERVER:
305 		return mbdyn_sasl_server_init(mbdyn_sasl);
306 		break;
307 
308 	case MBDYN_SASL_CLIENT:
309 		return mbdyn_sasl_client_init(mbdyn_sasl);
310 		break;
311 
312 	default:
313 		/* if SASL is critical, return SASL_FAIL */
314 		if (mbdyn_sasl->sasl_flags & MBDYN_SASL_FLAG_CRITICAL) {
315 			return SASL_FAIL;
316 		}
317 		return SASL_OK;
318 	}
319 }
320 
321 int
mbdyn_sasl_fini(void)322 mbdyn_sasl_fini(void)
323 {
324 	sasl_done();
325 
326 	return SASL_OK;
327 }
328 
329 int
mbdyn_sasl_client_auth(int sock,struct sockaddr * bindaddr,struct mbdyn_sasl_t * mbdyn_sasl)330 mbdyn_sasl_client_auth(int sock, struct sockaddr *bindaddr,
331 		struct mbdyn_sasl_t *mbdyn_sasl)
332 {
333 	int		rc = 0;
334 	sasl_conn_t	*conn = NULL;
335 	sasl_interact_t	*client_interact = NULL;
336 	const char	*out = NULL, *mechusing = NULL;
337 	unsigned	outlen = 0;
338 #if 0	/* will be used later */
339 	sasl_security_properties_t secprops;
340 #endif
341 
342 	int		count = 0;
343 	char		op;
344 	char		serverin[MBDYN_SASL_BUFSIZE] = { '\0' };
345 	const char		*mech = NULL;
346 	unsigned	serverinlen = 0;
347 
348 	int		oldflags, currflags;
349 
350 	oldflags = fcntl (sock, F_GETFL, 0);
351 	if (oldflags == -1) {
352 		return SASL_FAIL;
353 	}
354 	currflags = oldflags & ~O_NONBLOCK;
355 	if (fcntl(sock, F_SETFL, currflags) == -1) {
356 		return SASL_FAIL;
357 	}
358 
359 	rc = sasl_client_new(MBDYN_SASL_SERVICE,	/* name of service */
360 			mbdyn_sasl->sasl_hostname,
361 					/* The fully qualified domain name
362 					 * of the server we're connecting to */
363 			mbdyn_sasl->sasl_local_ip,
364 			mbdyn_sasl->sasl_remote_ip,
365 					/* Local and remote IP address strings
366 					 * (NULL disables mechanisms which
367 					 * require this info)*/
368 			NULL,		/* connection-specific callbacks */
369 			0, 		/* security flags */
370 			&conn);		/* allocated on success */
371 	if (rc != SASL_OK) {
372 		printf("[client] sasl_client_new() failed: %d (%s)\n",
373 				rc, sasl_errstring(rc, NULL, NULL));
374 		goto cleanup;
375 	}
376 
377 retry:;
378 	count = write(sock, "M", 1);
379 	if (count != 1) {
380 		if (count == -1) {
381 			if (errno == EINVAL && mbdyn_sasl->sasl_usleep) {
382 				usleep(mbdyn_sasl->sasl_usleep);
383 				goto retry;
384 			}
385 			printf("[client] write() failed errno=%d\n", errno);
386 		} else {
387 			printf("[client] write() failed (%d instead of %d)\n", count , 1);
388 		}
389 		rc = SASL_FAIL;
390 		goto cleanup;
391 	}
392 
393 	count = read(sock, &op, 1);
394 	if (count != 1) {
395 		if (count == -1) {
396 			printf("[client] read(M) failed errno=%d\n", errno);
397 		} else {
398 			printf("[client] read(M) failed (%d instead of %d)\n", count, 1);
399 		}
400 		rc = SASL_FAIL;
401 		goto cleanup;
402 	}
403 	if (op != 'M') {
404 		if (op != 'A') {	/* 'A' means abort */
405 			printf("[client] expecting \"M\" with methods\n");
406 		}
407 		rc = SASL_FAIL;
408 		goto cleanup;
409 	}
410 
411 	count = read(sock, &serverinlen, sizeof(unsigned));
412 	if (count != sizeof(unsigned)) {
413 		if (count == -1) {
414 			printf("[client] read(Ml) failed errno=%d\n", errno);
415 		} else {
416 			printf("[client] read(Ml) failed (%d instead of %u)\n",
417 				count, (unsigned)sizeof(unsigned));
418 		}
419 		rc = SASL_FAIL;
420 		goto cleanup;
421 	}
422 	if (serverinlen >= sizeof(serverin)) {
423 		printf("[client] buffer for mechanism list is too small\n");
424 		rc = SASL_FAIL;
425 		goto cleanup;
426 	}
427 	count = read(sock, serverin, serverinlen);
428 	if (count < 0 || (unsigned)count != serverinlen) {
429 		if (count < 0) {
430 			printf("[client] read(Md) failed errno=%d\n", errno);
431 		} else {
432 			printf("[client] read(Md) failed (%d instead of %d)\n",
433 				count, serverinlen);
434 		}
435 		rc = SASL_FAIL;
436 		goto cleanup;
437 	}
438 	serverin[count] = '\0';
439 
440 	mech = mbdyn_sasl->sasl_mech;
441 	if (!mech) {
442 		mech = serverin;
443 	}
444 
445 #if 0	/* will be used later */
446 	memset(&secprops, 0, sizeof(secprops));
447 	secprops.min_ssf = 1;
448 	secprops.max_ssf = 256;
449 	secprops.maxbufsize = MBDYN_SASL_BUFSIZE;
450 
451 	secprops.property_names = NULL;
452 	secprops.property_values = NULL;
453 	secprops.security_flags = SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT | SASL_SEC_MUTUAL_AUTH; /* as appropriate */
454 
455 	sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
456 #endif
457 
458 	do {
459 		char buf[MBDYN_SASL_BUFSIZE];
460 
461 		rc = sasl_client_start(conn,	/* same context from above */
462 				mech,		/* the list of mechanisms
463 						 * from the server */
464 				&client_interact,	/* filled in if an
465 							 * interaction
466 							 * is needed */
467 				&out,		/* filled in on success */
468 				&outlen,	/* filled in on success */
469 				&mechusing);	/* selected mech */
470 
471 		if (rc == SASL_INTERACT) {
472 			if (!(mbdyn_sasl->sasl_flags & MBDYN_SASL_FLAG_INTERACT)) {
473 				printf("[client] interaction disabled\n");
474 				rc = SASL_FAIL;
475 				goto cleanup;
476 			}
477 
478 			/* FIXME: handle interaction ... */
479 			printf("[client] sasl_client_start() requested "
480 					"interaction \"%s\"; mech=%s\n",
481 					client_interact->prompt, mechusing);
482 		}
483 
484 		if (outlen >= sizeof(buf)) {
485 			printf("[client] buf is too small\n");
486 			rc = SASL_FAIL;
487 			goto cleanup;
488 		}
489 		memcpy(buf, out, outlen);
490 		buf[outlen] = '\0';
491 	} while (rc == SASL_INTERACT);
492 
493 	switch (rc) {
494 	case SASL_FAIL:
495 		goto cleanup;
496 
497 	case SASL_OK:
498 	case SASL_CONTINUE:
499 		break;
500 
501 	default:
502 		printf("[client] sasl start failure\n");
503 		rc = SASL_FAIL;
504 		goto cleanup;
505 	}
506 
507 	count = write(sock, "S", 1);
508 	if (count != 1) {
509 		if (count == -1) {
510 			printf("[client] write() failed errno=%d\n", errno);
511 		} else {
512 			printf("[client] write() failed (%d instead of %d)\n", count, 1);
513 		}
514 		rc = SASL_FAIL;
515 		goto cleanup;
516 	}
517 	serverinlen = strlen(mechusing);
518 	count = write(sock, &serverinlen, sizeof(unsigned));
519 	if (count != sizeof(unsigned)) {
520 		if (count == -1) {
521 			printf("[client] write() failed errno=%d\n", errno);
522 		} else {
523 			printf("[client] write() failed (%d instead of %u)\n",
524 				count, (unsigned)sizeof(unsigned));
525 		}
526 		rc = SASL_FAIL;
527 		goto cleanup;
528 	}
529 	count = write(sock, mechusing, serverinlen);
530 	if (count < 0 || (unsigned)count != serverinlen) {
531 		if (count == -1) {
532 			printf("[client] mech=\"%s\" write failed errno=%d\n",
533 				mechusing, errno);
534 		} else {
535 			printf("[client] mech=\"%s\" write failed "
536 				"(%d instead of %d)\n",
537 				mechusing, count, serverinlen);
538 		}
539 		rc = SASL_FAIL;
540 		goto cleanup;
541 	}
542 
543 	count = write(sock, &outlen, sizeof(unsigned));
544 	if (count < 0 || (unsigned)count != sizeof(unsigned)) {
545 		if (count == -1) {
546 			printf("[client] write() failed errno=%d\n", errno);
547 		} else {
548 			printf("[client] write() failed (%d instead of %u)\n",
549 				count, (unsigned)sizeof(unsigned));
550 		}
551 		rc = SASL_FAIL;
552 		goto cleanup;
553 	}
554 	count = write(sock, out ? out : "", outlen);
555 	if (count < 0 || (unsigned)count != outlen) {
556 		if (count == -1) {
557 			printf("[client] mech=\"%s\" write failed errno=%d\n",
558 				mechusing, errno);
559 		} else {
560 			printf("[client] mech=\"%s\" write failed "
561 				"(%d instead of %d)\n",
562 				mechusing, count, outlen);
563 		}
564 		rc = SASL_FAIL;
565 		goto cleanup;
566 	}
567 
568 	do {
569 		serverin[0] = '\0';
570 		serverinlen = 0;
571 
572 		count = read(sock, &op, 1);
573 		if (count != 1) {
574 			if (count == -1) {
575 				printf("[client] read(c) failed errno=%d\n", errno);
576 			} else {
577 				printf("[client] read(c) failed (%d instead of %d)\n", count, 1);
578 			}
579 			rc = SASL_FAIL;
580 			goto cleanup;
581 		}
582 
583 		switch (op) {
584 		case 'O':	/* ok; but continue, to check the library's response ... */
585 			if (rc == SASL_OK) {
586 				goto cleanup;
587 			}
588 			break;
589 
590 		case 'F':	/* fail */
591 		case 'A':	/* abort */
592 			rc = SASL_FAIL;
593 			goto cleanup;
594 
595 		case 'C':	/* continue ... */
596 			count = read(sock, serverin, sizeof(unsigned));
597 			if (count != sizeof(unsigned)) {
598 				if (count == -1) {
599 					printf("[client] read(Cd) failed errno=%d\n", errno);
600 				} else {
601 					printf("[client] read(Cd) failed "
602 						"(%d instead of %u)\n",
603 						count, (unsigned)sizeof(unsigned));
604 				}
605 				rc = SASL_FAIL;
606 				goto cleanup;
607 			}
608 			serverinlen = ((unsigned *)(serverin))[0];
609 			if (serverinlen >= sizeof(serverin)) {
610 				printf("[client] buffer for "
611 						"sasl_client_step() "
612 						"continuation data "
613 						"is too small\n");
614 				rc = SASL_FAIL;
615 				goto cleanup;
616 			}
617 			count = read(sock, serverin, serverinlen);
618 			if (count < 0 || (unsigned)count != serverinlen) {
619 				if (count == -1) {
620 					printf("[client] read() failed errno=%d\n", errno);
621 				} else {
622 					printf("[client] read() failed (%d instead of %d)\n",
623 						count, serverinlen);
624 				}
625 				rc = SASL_FAIL;
626 				goto cleanup;
627 			}
628 			serverin[serverinlen] = '\0';
629 			break;
630 
631 		default:
632 			break;
633 		}
634 
635 		rc = sasl_client_step(conn,	/* our context */
636 				serverin,	/* the data from the server */
637 				serverinlen,	/* its length */
638 				&client_interact,	/* this should be
639 							 * unallocated
640 							 * and NULL */
641 				&out,		/* filled in on success */
642 				&outlen);	/* filled in on success */
643 
644 		count = write(sock, "C", 1);
645 		if (count != 1) {
646 			if (count == -1) {
647 				printf("[client] write failed errno=%d\n", errno);
648 			} else {
649 				printf("[client] write failed (%d instead of %d)\n", count, 1);
650 			}
651 			rc = SASL_FAIL;
652 			goto cleanup;
653 		}
654 
655 		count = write(sock, &outlen, sizeof(unsigned));
656 		if (count != sizeof(unsigned)) {
657 			if (count == -1) {
658 				printf("[client] write failed errno=%d\n", errno);
659 			} else {
660 				printf("[client] write failed "
661 					"(%d instead of %u)\n",
662 					count, (unsigned)sizeof(unsigned));
663 			}
664 			rc = SASL_FAIL;
665 			goto cleanup;
666 		}
667 		count = write(sock, out ? out : "", outlen);
668 		if (count < 0 || (unsigned)count != outlen) {
669 			if (count == -1) {
670 				printf("[client] write failed errno=%d\n", errno);
671 			} else {
672 				printf("[client] write failed (%d instead of %d)\n", count, outlen);
673 			}
674 			rc = SASL_FAIL;
675 			goto cleanup;
676 		}
677 
678 		switch (rc) {
679 		case SASL_INTERACT:
680 			/* FIXME: interaction */
681 		case SASL_CONTINUE:
682 			break;
683 
684 		case SASL_OK:
685 			break;
686 
687 		case SASL_FAIL:
688 			goto cleanup;
689 
690 		default:
691 			break;
692 		}
693 	} while (rc == SASL_INTERACT || rc == SASL_CONTINUE || rc == SASL_OK);
694 
695 	if (rc != SASL_OK) {
696 		printf("[client] sasl step failure\n");
697 		goto cleanup;
698 	}
699 
700 cleanup:;
701 
702 #if 0	/* will use later */
703 	if (rc == SASL_OK) {
704 		int i;
705 		char buf[MBDYN_SASL_BUFSIZE];
706 		int ssf = 0, outbufsize = MBDYN_SASL_BUFSIZE;
707 		const int *ssfp = &ssf, *outbufsizep = &outbufsize;
708 
709 		rc = sasl_getprop(conn, SASL_SSF, (const void **)&ssfp);
710 		if (rc != SASL_OK) {
711 			fprintf(stderr, "[client] unable to retrieve ssf\n");
712 			exit(EXIT_FAILURE);
713 		}
714 		if (*ssfp > 0) {
715 			ssf = *ssfp;
716 			rc = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **)&outbufsizep);
717 			if (rc != SASL_OK) {
718 				fprintf(stderr, "[client] unable to retrieve out buf size\n");
719 				exit(EXIT_FAILURE);
720 			}
721 			outbufsize = *outbufsizep;
722 		}
723 
724 		fprintf(stderr, "[client] SASL_OK (ssf=%d, outbufsize=%d)\n", ssf, outbufsize);
725 	}
726 #endif
727 
728 	/* FIXME: don't dispose if using encoding */
729 	if (conn) {
730 		sasl_dispose(&conn);
731 	}
732 
733 	if (fcntl (sock, F_SETFL, oldflags) == -1) {
734 		rc = SASL_FAIL;
735 	}
736 
737 	return rc;
738 }
739 
740 int
mbdyn_sasl_server_auth(int sock,struct sockaddr * bindaddr,struct mbdyn_sasl_t * mbdyn_sasl)741 mbdyn_sasl_server_auth(int sock, struct sockaddr *bindaddr,
742 		struct mbdyn_sasl_t *mbdyn_sasl)
743 {
744 	int		rc = SASL_FAIL;
745 	sasl_conn_t	*conn = NULL;
746 	const char	*result_string = NULL;
747 	unsigned	string_length = 0;
748 	int		number_of_mechanisms = 0;
749 	const char	*out = NULL;
750 	unsigned	outlen = 0;
751 #if 0	/* will be used later */
752 	sasl_security_properties_t secprops;
753 #endif
754 
755 	int		count = 0;
756 	char		op = '\0';
757 	char		mechanism_client_chose[MBDYN_SASL_BUFSIZE] = { '\0' };
758 	char		clientin[MBDYN_SASL_BUFSIZE] = { '\0' };
759 	unsigned	clientinlen = 0;
760 
761 	int		oldflags, currflags;
762 
763 	oldflags = fcntl (sock, F_GETFL, 0);
764 	if (oldflags == -1) {
765 		return SASL_FAIL;
766 	}
767 	currflags = oldflags & ~O_NONBLOCK;
768 	if (fcntl(sock, F_SETFL, currflags) == -1) {
769 		return SASL_FAIL;
770 	}
771 
772 	rc = sasl_server_new(MBDYN_SASL_SERVICE,	/* name of service */
773 		NULL,			/* my fully qualified domain name;
774 					 * NULL says use gethostname() */
775 		mbdyn_sasl->sasl_realm,/* The user realm used for password
776 		                         * lookups; NULL means default
777 					 * to serverFQDN.  Note: This does
778 					 * not affect Kerberos */
779 		mbdyn_sasl->sasl_local_ip,
780 		mbdyn_sasl->sasl_remote_ip,	/* IP Address information
781 						 * strings */
782 		NULL,			/* Callbacks supported only
783 					 * for this connection */
784 		0,			/* security flags (security layers
785 					   are enabled using security
786 					   properties, separately) */
787 		&conn);			/* allocated on success */
788 	if (rc != SASL_OK) {
789 		printf("[server] sasl_server_new() failed: %d (%s)\n",
790 				rc, sasl_errstring(rc, NULL, NULL));
791 		goto cleanup;
792 	}
793 
794 #if 0	/* will be used later */
795 	memset(&secprops, 0, sizeof(secprops));
796 	secprops.min_ssf = 1;
797 	secprops.max_ssf = 256;
798 	secprops.maxbufsize = MBDYN_SASL_BUFSIZE;
799 
800 	secprops.property_names = NULL;
801 	secprops.property_values = NULL;
802 	secprops.security_flags = SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT | SASL_SEC_MUTUAL_AUTH; /* as appropriate */
803 
804 	sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
805 #endif
806 
807 retry:;
808 	count = read(sock, &op, 1);
809 	if (count != 1) {
810 		if (count == -1) {
811 			if (errno == EINVAL && mbdyn_sasl->sasl_usleep) {
812 				usleep(mbdyn_sasl->sasl_usleep);
813 				goto retry;
814 			}
815 			printf("[server] read failed errno=%d\n", errno);
816 		} else {
817 			printf("[server] read failed (%d instead of %d)\n", count, 2);
818 		}
819 		rc = SASL_FAIL;
820 		goto cleanup;
821 	}
822 
823 	if (op != 'M') {
824 		printf("[server] \"M\" expected\n");
825 		rc = SASL_FAIL;
826 		goto cleanup;
827 	}
828 
829 	rc = sasl_listmech(conn,	/* The context for this connection */
830 			NULL,		/* not supported */
831 			"",		/* What to prepend the string with */
832 			" ",		/* What to separate mechanisms with */
833 			"",		/* What to append to the string */
834 			&result_string,	/* The produced string. */
835 			&string_length,	/* length of the string */
836 			&number_of_mechanisms); /* Number of mechanisms
837 						 * in the string */
838 	if (rc != SASL_OK) {
839 		printf("[server] sasl_listmech() failed: %s\n",
840 				sasl_errdetail(conn));
841 		rc = SASL_FAIL;
842 		goto cleanup;
843 	}
844 
845 	count = write(sock, "M", 1);
846 	if (count != 1) {
847 		if (count == -1) {
848 			printf("[server] write() failed errno=%d\n", errno);
849 		} else {
850 			printf("[server] write() failed (%d instead of %d)\n", count, 1);
851 		}
852 		rc = SASL_FAIL;
853 		goto cleanup;
854 	}
855 	count = write(sock, &string_length, sizeof(unsigned));
856 	if (count != sizeof(unsigned)) {
857 		if (count == -1) {
858 			printf("[server] write() failed errno=%d\n", errno);
859 		} else {
860 			printf("[server] write() failed (%d instead of %u)\n",
861 				count, (unsigned)sizeof(unsigned));
862 		}
863 		rc = SASL_FAIL;
864 		goto cleanup;
865 	}
866 	count = write(sock, result_string, string_length);
867 	if (count < 0 || (unsigned)count != string_length) {
868 		if (count == -1) {
869 			printf("[server] write() failed errno=%d\n", errno);
870 		} else {
871 			printf("[server] write() failed (%d instead of %d)\n",
872 				count, string_length);
873 		}
874 		rc = SASL_FAIL;
875 		goto cleanup;
876 	}
877 
878 	/* read client's choice and more */
879 	count = read(sock, &op, 1);
880 	if (count != 1) {
881 		if (count == -1) {
882 			printf("[server] read() failed errno=%d\n", errno);
883 		} else {
884 			printf("[server] read() failed (%d instead of %d)\n",
885 				count, 1);
886 		}
887 		rc = SASL_FAIL;
888 		goto cleanup;
889 	}
890 	if (op != 'S') {
891 		/* send abort? */
892 		if (op != 'A') {
893 			printf("[server] expecting \"S\" to start auth\n");
894 		}
895 		rc = SASL_FAIL;
896 		goto cleanup;
897 	}
898 	count = read(sock, &clientinlen, sizeof(unsigned));
899 	if (count != sizeof(unsigned)) {
900 		if (count == -1) {
901 			printf("[server] read() failed errno=%d\n", errno);
902 		} else {
903 			printf("[server] read() failed (%d instead of %u)\n",
904 				count, (unsigned)sizeof(unsigned));
905 		}
906 		rc = SASL_FAIL;
907 		goto cleanup;
908 	}
909 	if (clientinlen >= sizeof(mechanism_client_chose)) {
910 		printf("[server] buffer for mechanism too small\n");
911 		rc = SASL_FAIL;
912 		goto cleanup;
913 	}
914 	count = read(sock, mechanism_client_chose, clientinlen);
915 	if (count < 0 || (unsigned)count != clientinlen) {
916 		if (count == -1) {
917 			printf("[server] read() failed errno=%d\n", errno);
918 		} else {
919 			printf("[server] read() failed (%d instead of %u)\n",
920 				count, clientinlen);
921 		}
922 		rc = SASL_FAIL;
923 		goto cleanup;
924 	}
925 	mechanism_client_chose[clientinlen] = '\0';
926 
927 	/* read client's additional string ... */
928 	count = read(sock, &clientinlen, sizeof(unsigned));
929 	if (count != sizeof(unsigned)) {
930 		if (count == -1) {
931 			printf("[server] read() failed errno=%d\n", errno);
932 		} else {
933 			printf("[server] read() failed (%d instead of %u)\n",
934 				count, (unsigned)sizeof(unsigned));
935 		}
936 		rc = SASL_FAIL;
937 		goto cleanup;
938 	}
939 	if (clientinlen >= sizeof(clientin)) {
940 		printf("[server] buffer for client optional string "
941 				"is too small\n");
942 		rc = SASL_FAIL;
943 		goto cleanup;
944 	}
945 	count = read(sock, clientin, clientinlen);
946 	if (count < 0 || (unsigned)count != clientinlen) {
947 		if (count == -1) {
948 			printf("[server] read() failed errno=%d\n", errno);
949 		} else {
950 			printf("[server] read() failed (%d instead of %u)\n",
951 				count, clientinlen);
952 		}
953 		rc = SASL_FAIL;
954 		goto cleanup;
955 	}
956 	clientin[clientinlen] = '\0';
957 
958 	rc = sasl_server_start(conn,		/* context */
959 			mechanism_client_chose,	/* selected mechanism */
960 			clientinlen ? clientin : NULL,	/* the optional string
961 							 * the client gave us */
962 			clientinlen,			/* and its length */
963 			&out,		/* The output of the library.
964 					 * Might not be NULL terminated */
965 			&outlen);	/* its lenght */
966 
967 	do {
968 		switch (rc) {
969 		case SASL_OK:
970 			count = write(sock, "O", 1);
971 			if (count != 1) {
972 				if (count == -1) {
973 					printf("[server] write() failed errno=%d\n", errno);
974 				} else {
975 					printf("[server] write() failed (%d instead of %d)\n", count, 1);
976 				}
977 				rc = SASL_FAIL;
978 			}
979 			goto cleanup;
980 
981 		case SASL_CONTINUE: {
982 			char buf[MBDYN_SASL_BUFSIZE];
983 
984 			if (1 + outlen + sizeof(unsigned) >= sizeof(buf)) {
985 				printf("[server] buffer for "
986 						"sasl_server_step() "
987 						"continuation data "
988 						"is too small\n");
989 				rc = SASL_FAIL;
990 				goto cleanup;
991 			}
992 
993 			memcpy(buf, "C", 1);
994 			memcpy(buf + 1, &outlen, sizeof(unsigned));
995 			memcpy(buf + 1 + sizeof(unsigned), out, outlen);
996 
997 			count = write(sock, buf, 1 + outlen + sizeof(unsigned));
998 			if (count < 0 || (unsigned)count != 1 + outlen + sizeof(unsigned)) {
999 				if (count == -1) {
1000 					printf("[server] write() failed errno=%d\n", count);
1001 				} else {
1002 					printf("[server] write() failed "
1003 						"(%d instead of %u)\n",
1004 						count,
1005 						(unsigned)(1 + outlen + sizeof(unsigned)));
1006 				}
1007 				rc = SASL_FAIL;
1008 				goto cleanup;
1009 			}
1010 			}
1011 			break;
1012 
1013 		default:
1014 			count = write(sock, "F", 1);
1015 			if (count != 1) {
1016 				if (count == -1) {
1017 					printf("[server] write() failed errno=%d\n", errno);
1018 				} else {
1019 					printf("[server] write() failed (%d instead of %d)\n", count, 1);
1020 				}
1021 				rc = SASL_FAIL;
1022 			}
1023 			goto cleanup;
1024 		}
1025 
1026 		count = read(sock, &op, 1);
1027 		if (count != 1) {
1028 			if (count == -1) {
1029 				printf("[server] read() failed errno=%d\n", errno);
1030 			} else {
1031 				printf("[server] read() failed (%d instead of %d)\n", count, 1);
1032 			}
1033 			rc = SASL_FAIL;
1034 			goto cleanup;
1035 		}
1036 		if (op != 'C') {
1037 			if (op != 'A') {
1038 				printf("[server] expecting \"C\" to continue\n");
1039 			}
1040 			rc = SASL_FAIL;
1041 			goto cleanup;
1042 		}
1043 
1044 		clientin[0] = '\0';
1045 		clientinlen = 0;
1046 
1047 		count = read(sock, &clientinlen, sizeof(unsigned));
1048 		if (count != sizeof(unsigned)) {
1049 			if (count == -1) {
1050 				printf("[server] read() failed errno=%d\n", errno);
1051 			} else {
1052 				printf("[server] read() failed "
1053 					"(%d instead of %u)\n",
1054 					count, (unsigned)sizeof(unsigned));
1055 			}
1056 			rc = SASL_FAIL;
1057 			goto cleanup;
1058 		}
1059 		if (clientinlen >= sizeof(clientin)) {
1060 			printf("[server] buffer for sasl_server_step() "
1061 					"client data is too small\n");
1062 			rc = SASL_FAIL;
1063 			goto cleanup;
1064 		}
1065 		count = read(sock, clientin, clientinlen);
1066 		if (count < 0 || (unsigned)count != clientinlen) {
1067 			if (count == -1) {
1068 				printf("[server] read() failed errno=%d\n", errno);
1069 			} else {
1070 				printf("[server] read() failed "
1071 					"(%d instead of %u)\n",
1072 					count, clientinlen);
1073 			}
1074 			rc = SASL_FAIL;
1075 			goto cleanup;
1076 		}
1077 
1078 		rc = sasl_server_step(conn,	/* context */
1079 				clientin,	/* what the client gave */
1080 				clientinlen,	/* its length */
1081 				&out,		/* allocated by library
1082 						 * on success.  Might not
1083 						 * be NULL terminated */
1084 				&outlen);	/* its length */
1085 	} while (1);
1086 
1087 cleanup:;
1088 	/* FIXME: don't dispose if encoding connection */
1089 	if (conn) {
1090 		sasl_dispose(&conn);
1091 	}
1092 
1093 	if (fcntl (sock, F_SETFL, oldflags) == -1) {
1094 		rc = SASL_FAIL;
1095 	}
1096 
1097 	return rc;
1098 }
1099 
1100 int
mbdyn_sasl_auth(int sock,struct sockaddr * bindaddr,struct mbdyn_sasl_t * mbdyn_sasl)1101 mbdyn_sasl_auth(int sock, struct sockaddr *bindaddr,
1102 		struct mbdyn_sasl_t *mbdyn_sasl)
1103 {
1104 	switch (mbdyn_sasl->use_sasl) {
1105 	case MBDYN_SASL_SERVER:
1106 		return mbdyn_sasl_server_auth(sock, bindaddr, mbdyn_sasl);
1107 		break;
1108 
1109 	case MBDYN_SASL_CLIENT:
1110 		return mbdyn_sasl_client_auth(sock, bindaddr, mbdyn_sasl);
1111 		break;
1112 
1113 	default:
1114 		/* if SASL is critical, return SASL_FAIL */
1115 		if (mbdyn_sasl->sasl_flags & MBDYN_SASL_FLAG_CRITICAL) {
1116 			return SASL_FAIL;
1117 		}
1118 		return SASL_OK;
1119 	}
1120 }
1121 
1122 int
mbdyn_sasl_validate(struct mbdyn_sasl_t * mbdyn_sasl)1123 mbdyn_sasl_validate(struct mbdyn_sasl_t *mbdyn_sasl)
1124 {
1125 	switch (mbdyn_sasl->use_sasl) {
1126 	case MBDYN_SASL_NONE:
1127 		return SASL_FAIL;
1128 
1129 	case MBDYN_SASL_SERVER:
1130 		if (mbdyn_sasl->sasl_user != NULL) {
1131 			return SASL_FAIL;
1132 		}
1133 
1134 		if (mbdyn_sasl->sasl_cred != NULL) {
1135 			return SASL_FAIL;
1136 		}
1137 
1138 		if (mbdyn_sasl->sasl_authz != NULL) {
1139 			return SASL_FAIL;
1140 		}
1141 
1142 		return SASL_OK;
1143 
1144 	case MBDYN_SASL_CLIENT:
1145 		if (mbdyn_sasl->sasl_hostname == NULL) {
1146 			mbdyn_sasl->sasl_hostname = "localhost";
1147 		}
1148 
1149 		return SASL_OK;
1150 
1151 	}
1152 
1153 	return SASL_FAIL;
1154 }
1155 
1156 /*
1157  * a=<authz>	client: authorization identity (optional)
1158  * f=<flag>[=<value>]
1159  * i=<remoteip>	remote ip
1160  * l=<localip>	local ip
1161  * m=<method>	(list of) acceptable method(s)
1162  * r=<realm>	client: user realm;
1163  * 		server: server realm
1164  * s={server|client}	use SASL to negotiate auth
1165  * 			as server or client
1166  * u=<user>	client: user identity
1167  * w=<cred>	client: user credential
1168  */
1169 int
mbdyn_sasl_parse_args(int opt,const char * optarg,struct mbdyn_sasl_t * mbdyn_sasl)1170 mbdyn_sasl_parse_args(int opt, const char *optarg,
1171 		struct mbdyn_sasl_t *mbdyn_sasl)
1172 {
1173 	switch (opt) {
1174 	case 'a':
1175 		mbdyn_sasl->sasl_authz = optarg[0] ? optarg : NULL;
1176 		break;
1177 
1178 	case 'f':
1179 		if (strcasecmp(optarg, "userauthz") == 0) {
1180 			mbdyn_sasl->sasl_flags |= MBDYN_SASL_FLAG_USERAUTHZ;
1181 
1182 		} else if (strcasecmp(optarg, "interact") == 0) {
1183 			mbdyn_sasl->sasl_flags |= MBDYN_SASL_FLAG_INTERACT;
1184 
1185 		} else {
1186 			printf("UNKNOWN FLAG '%s'\n", optarg);
1187 		}
1188 		break;
1189 
1190 	case 'h':
1191 		mbdyn_sasl->sasl_hostname = optarg[0] ? optarg : NULL;
1192 		break;
1193 
1194 	case 'i':
1195 		mbdyn_sasl->sasl_remote_ip = optarg[0] ? optarg : NULL;
1196 		break;
1197 
1198 	case 'l':
1199 		mbdyn_sasl->sasl_local_ip = optarg[0] ? optarg : NULL;
1200 		break;
1201 
1202 	case 'm':
1203 		mbdyn_sasl->sasl_mech = optarg[0] ? optarg : NULL;
1204 		break;
1205 
1206 	case 'r':
1207 		mbdyn_sasl->sasl_realm = optarg[0] ? optarg : NULL;
1208 		break;
1209 
1210 	case 's':
1211 		if (strcasecmp(optarg, "server") == 0) {
1212 			mbdyn_sasl->use_sasl = MBDYN_SASL_SERVER;
1213 		} else if (strcasecmp(optarg, "client") == 0) {
1214 			mbdyn_sasl->use_sasl = MBDYN_SASL_CLIENT;
1215 		} else if (strcasecmp(optarg, "none") == 0) {
1216 			mbdyn_sasl->use_sasl = MBDYN_SASL_NONE;
1217 		} else {
1218 			printf("UNKNOWN SASL MODE; SASL DISABLED\n");
1219 			mbdyn_sasl->use_sasl = MBDYN_SASL_NONE;
1220 		}
1221 		break;
1222 
1223 	case 't': {
1224 		char		*next = NULL;
1225 		unsigned long	l;
1226 
1227 		errno = 0;
1228 		l = strtoul(optarg, &next, 10);
1229 		int save_errno = errno;
1230 		if (next == NULL || next[0] != '\0') {
1231 			printf("ILLEGAL SLEEP TIME '%s'\n", optarg);
1232 
1233 		} else if (save_errno == ERANGE) {
1234 			printf("SLEEP TIME '%s' OVERFLOWS\n", optarg);
1235 
1236 		} else {
1237 			mbdyn_sasl->sasl_usleep = l;
1238 		}
1239 		}
1240 		break;
1241 
1242 	case 'u':
1243 		mbdyn_sasl->sasl_user = optarg[0] ? optarg : NULL;
1244 		break;
1245 
1246 	case 'w':
1247 		mbdyn_sasl->sasl_cred = optarg[0] ? optarg : NULL;
1248 		break;
1249 
1250 	default:
1251 		return -1;
1252 	}
1253 
1254 	return 0;
1255 }
1256 
1257 #endif /* HAVE_SASL2 */
1258 
1259