1 /* sample-server.c -- sample SASL server
2 * Rob Earhart
3 */
4 /*
5 * Copyright (c) 1998-2016 Carnegie Mellon University. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The name "Carnegie Mellon University" must not be used to
20 * endorse or promote products derived from this software without
21 * prior written permission. For permission or any other legal
22 * details, please contact
23 * Carnegie Mellon University
24 * Center for Technology Transfer and Enterprise Creation
25 * 4615 Forbes Avenue
26 * Suite 302
27 * Pittsburgh, PA 15213
28 * (412) 268-7393, fax: (412) 268-7395
29 * innovation@andrew.cmu.edu
30 *
31 * 4. Redistributions of any form whatsoever must retain the following
32 * acknowledgment:
33 * "This product includes software developed by Computing Services
34 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35 *
36 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43 */
44
45 #include <config.h>
46 #include <limits.h>
47 #include <stdio.h>
48
49 #ifdef HAVE_GETOPT_H
50 #include <getopt.h>
51 #endif
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55
56 #ifdef WIN32
57 # include <winsock2.h>
58 __declspec(dllimport) char *optarg;
59 __declspec(dllimport) int optind;
60 __declspec(dllimport) int getsubopt(char **optionp, const char * const *tokens, char **valuep);
61 #define HAVE_GETSUBOPT
62 #else /* WIN32 */
63 # include <netinet/in.h>
64 #endif /* WIN32 */
65 #include <sasl.h>
66 #include <saslplug.h>
67 #include <saslutil.h>
68
69 #ifndef HAVE_GETSUBOPT
70 int getsubopt(char **optionp, const char * const *tokens, char **valuep);
71 #endif
72
73 static const char *progname = NULL;
74 static int verbose;
75
76 /* Note: if this is changed, change it in samp_read(), too. */
77 #define SAMPLE_SEC_BUF_SIZE (2048)
78
79 static const char
80 message[] = "Come here Watson, I want you.";
81
82 char buf[SAMPLE_SEC_BUF_SIZE];
83
84 static const char *bit_subopts[] = {
85 #define OPT_MIN (0)
86 "min",
87 #define OPT_MAX (1)
88 "max",
89 NULL
90 };
91
92 static const char *ext_subopts[] = {
93 #define OPT_EXT_SSF (0)
94 "ssf",
95 #define OPT_EXT_ID (1)
96 "id",
97 NULL
98 };
99
100 static const char *flag_subopts[] = {
101 #define OPT_NOPLAIN (0)
102 "noplain",
103 #define OPT_NOACTIVE (1)
104 "noactive",
105 #define OPT_NODICT (2)
106 "nodict",
107 #define OPT_FORWARDSEC (3)
108 "forwardsec",
109 #define OPT_NOANONYMOUS (4)
110 "noanonymous",
111 #define OPT_PASSCRED (5)
112 "passcred",
113 NULL
114 };
115
116 static const char *ip_subopts[] = {
117 #define OPT_IP_LOCAL (0)
118 "local",
119 #define OPT_IP_REMOTE (1)
120 "remote",
121 NULL
122 };
123
124 char *mech = NULL,
125 *iplocal = NULL,
126 *ipremote = NULL,
127 *searchpath = NULL,
128 *service = "rcmd",
129 *localdomain = NULL,
130 *userdomain = NULL;
131 sasl_conn_t *conn = NULL;
132
133 static void
free_conn(void)134 free_conn(void)
135 {
136 if (conn)
137 sasl_dispose(&conn);
138 }
139
140 static int
sasl_my_log(void * context,int priority,const char * message)141 sasl_my_log(void *context __attribute__((unused)),
142 int priority,
143 const char *message)
144 {
145 const char *label;
146
147 if (! message)
148 return SASL_BADPARAM;
149
150 switch (priority) {
151 case SASL_LOG_ERR:
152 label = "Error";
153 break;
154 case SASL_LOG_NOTE:
155 label = "Info";
156 break;
157 default:
158 label = "Other";
159 break;
160 }
161
162 fprintf(stderr, "%s: SASL %s: %s\n",
163 progname, label, message);
164
165 return SASL_OK;
166 }
167
168 static int
getpath(void * context,char ** path)169 getpath(void *context __attribute__((unused)),
170 char ** path)
171 {
172 if (! path)
173 return SASL_BADPARAM;
174
175 if (searchpath) {
176 *path = searchpath;
177 } else {
178 *path = PLUGINDIR;
179 }
180
181 return SASL_OK;
182 }
183
184 static sasl_callback_t callbacks[] = {
185 {
186 SASL_CB_LOG, (sasl_callback_ft)&sasl_my_log, NULL
187 }, {
188 SASL_CB_GETPATH, (sasl_callback_ft)&getpath, NULL
189 }, {
190 SASL_CB_LIST_END, NULL, NULL
191 }
192 };
193
194 static void
sasldebug(int why,const char * what,const char * errstr)195 sasldebug(int why, const char *what, const char *errstr)
196 {
197 fprintf(stderr, "%s: %s: %s",
198 progname,
199 what,
200 sasl_errstring(why, NULL, NULL));
201 if (errstr)
202 fprintf(stderr, " (%s)\n", errstr);
203 else
204 putc('\n', stderr);
205 }
206
207 static void
saslfail(int why,const char * what,const char * errstr)208 saslfail(int why, const char *what, const char *errstr)
209 {
210 sasldebug(why, what, errstr);
211 exit(EXIT_FAILURE);
212 }
213
214 static void
fail(const char * what)215 fail(const char *what)
216 {
217 fprintf(stderr, "%s: %s\n",
218 progname, what);
219 exit(EXIT_FAILURE);
220 }
221
222 static void
osfail()223 osfail()
224 {
225 perror(progname);
226 exit(EXIT_FAILURE);
227 }
228
229 static void
samp_send(const char * buffer,unsigned length)230 samp_send(const char *buffer,
231 unsigned length)
232 {
233 char *buf;
234 unsigned len, alloclen;
235 int result;
236
237 alloclen = ((length / 3) + 1) * 4 + 1;
238 buf = malloc(alloclen);
239 if (! buf)
240 osfail();
241
242 result = sasl_encode64(buffer, length, buf, alloclen, &len);
243 if (result != SASL_OK)
244 saslfail(result, "Encoding data in base64", NULL);
245 printf("S: %s\n", buf);
246 free(buf);
247 }
248
249 static unsigned
samp_recv()250 samp_recv()
251 {
252 unsigned len;
253 int result;
254
255 if (! fgets(buf, SAMPLE_SEC_BUF_SIZE, stdin)) {
256 fail("Unable to parse input");
257 }
258
259 if (strncmp(buf, "C: ", 3) != 0) {
260 fail("Line must start with 'C: '");
261 }
262
263 len = strlen(buf);
264 if (len > 0 && buf[len-1] == '\n') {
265 buf[len-1] = '\0';
266 }
267
268 result = sasl_decode64(buf + 3, (unsigned) strlen(buf + 3), buf,
269 SAMPLE_SEC_BUF_SIZE, &len);
270 if (result != SASL_OK)
271 saslfail(result, "Decoding data from base64", NULL);
272 buf[len] = '\0';
273 printf("got '%s'\n", buf);
274 return len;
275 }
276
277
278 int
main(int argc,char * argv[])279 main(int argc, char *argv[])
280 {
281 int c = 0;
282 int errflag = 0;
283 int result;
284 sasl_security_properties_t secprops;
285 sasl_ssf_t extssf = 0;
286 const char *ext_authid = NULL;
287 char *options, *value;
288 unsigned len, count;
289 const char *data;
290 int serverlast = 0;
291 sasl_ssf_t *ssf;
292
293 #ifdef WIN32
294 /* initialize winsock */
295 WSADATA wsaData;
296
297 result = WSAStartup( MAKEWORD(2, 0), &wsaData );
298 if ( result != 0) {
299 saslfail(SASL_FAIL, "Initializing WinSockets", NULL);
300 }
301 #endif
302
303 progname = strrchr(argv[0], HIER_DELIMITER);
304 if (progname)
305 progname++;
306 else
307 progname = argv[0];
308
309 /* Init defaults... */
310 memset(&secprops, 0L, sizeof(secprops));
311 secprops.maxbufsize = SAMPLE_SEC_BUF_SIZE;
312 secprops.max_ssf = UINT_MAX;
313
314 verbose = 0;
315 while ((c = getopt(argc, argv, "vlhb:e:m:f:i:p:s:d:u:?")) != EOF)
316 switch (c) {
317 case 'v':
318 verbose = 1;
319 break;
320 case 'b':
321 options = optarg;
322 while (*options != '\0')
323 switch(getsubopt(&options, (const char * const *)bit_subopts, &value)) {
324 case OPT_MIN:
325 if (! value)
326 errflag = 1;
327 else
328 secprops.min_ssf = atoi(value);
329 break;
330 case OPT_MAX:
331 if (! value)
332 errflag = 1;
333 else
334 secprops.max_ssf = atoi(value);
335 break;
336 default:
337 errflag = 1;
338 break;
339 }
340 break;
341
342 case 'e':
343 options = optarg;
344 while (*options != '\0')
345 switch(getsubopt(&options, (const char * const *)ext_subopts, &value)) {
346 case OPT_EXT_SSF:
347 if (! value)
348 errflag = 1;
349 else
350 extssf = atoi(value);
351 break;
352 case OPT_MAX:
353 if (! value)
354 errflag = 1;
355 else
356 ext_authid = value;
357 break;
358 default:
359 errflag = 1;
360 break;
361 }
362 break;
363
364 case 'm':
365 mech = optarg;
366 break;
367
368 case 'f':
369 options = optarg;
370 while (*options != '\0') {
371 switch(getsubopt(&options, (const char * const *)flag_subopts, &value)) {
372 case OPT_NOPLAIN:
373 secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
374 break;
375 case OPT_NOACTIVE:
376 secprops.security_flags |= SASL_SEC_NOACTIVE;
377 break;
378 case OPT_NODICT:
379 secprops.security_flags |= SASL_SEC_NODICTIONARY;
380 break;
381 case OPT_FORWARDSEC:
382 secprops.security_flags |= SASL_SEC_FORWARD_SECRECY;
383 break;
384 case OPT_NOANONYMOUS:
385 secprops.security_flags |= SASL_SEC_NOANONYMOUS;
386 break;
387 case OPT_PASSCRED:
388 secprops.security_flags |= SASL_SEC_PASS_CREDENTIALS;
389 break;
390 default:
391 errflag = 1;
392 break;
393 }
394 if (value) errflag = 1;
395 }
396 break;
397
398 case 'l':
399 serverlast = SASL_SUCCESS_DATA;
400 break;
401
402 case 'i':
403 options = optarg;
404 while (*options != '\0')
405 switch(getsubopt(&options, (const char * const *)ip_subopts, &value)) {
406 case OPT_IP_LOCAL:
407 if (! value)
408 errflag = 1;
409 else
410 iplocal = value;
411 break;
412 case OPT_IP_REMOTE:
413 if (! value)
414 errflag = 1;
415 else
416 ipremote = value;
417 break;
418 default:
419 errflag = 1;
420 break;
421 }
422 break;
423
424 case 'p':
425 searchpath = optarg;
426 break;
427
428 case 's':
429 service = optarg;
430 break;
431
432 case 'd':
433 localdomain = optarg;
434 break;
435
436 case 'u':
437 userdomain = optarg;
438 break;
439
440 default:
441 errflag = 1;
442 break;
443 }
444
445 if (optind != argc) {
446
447 errflag = 1;
448 }
449
450 if (errflag) {
451 fprintf(stderr, "%s: Usage: %s [-b min=N,max=N] [-e ssf=N,id=ID] [-m MECH] [-f FLAGS] [-i local=IP,remote=IP] [-p PATH] [-d DOM] [-u DOM] [-s NAME]\n"
452 "\t-b ...\t#bits to use for encryption\n"
453 "\t\tmin=N\tminimum #bits to use (1 => integrity)\n"
454 "\t\tmax=N\tmaximum #bits to use\n"
455 "\t-e ...\tassume external encryption\n"
456 "\t\tssf=N\texternal mech provides N bits of encryption\n"
457 "\t\tid=ID\texternal mech provides authentication id ID\n"
458 "\t-m MECH\tforce use of MECH for security\n"
459 "\t-f ...\tset security flags\n"
460 "\t\tnoplain\t\trequire security vs. passive attacks\n"
461 "\t\tnoactive\trequire security vs. active attacks\n"
462 "\t\tnodict\t\trequire security vs. passive dictionary attacks\n"
463 "\t\tforwardsec\trequire forward secrecy\n"
464 "\t\tmaximum\t\trequire all security flags\n"
465 "\t\tpasscred\tattempt to receive client credentials\n"
466 "\t-i ...\tset IP addresses (required by some mechs)\n"
467 "\t\tlocal=IP;PORT\tset local address to IP, port PORT\n"
468 "\t\tremote=IP;PORT\tset remote address to IP, port PORT\n"
469 "\t-p PATH\tcolon-seperated search path for mechanisms\n"
470 "\t-s NAME\tservice name to pass to mechanisms\n"
471 "\t-d DOM\tlocal server domain\n"
472 "\t-u DOM\tuser domain\n"
473 "\t-l\tenable server-send-last\n",
474 progname, progname);
475 exit(EXIT_FAILURE);
476 }
477
478 result = sasl_server_init(callbacks, "sample");
479 if (result != SASL_OK)
480 saslfail(result, "Initializing libsasl", NULL);
481
482 atexit(&sasl_done);
483
484 result = sasl_server_new(service,
485 localdomain,
486 userdomain,
487 iplocal,
488 ipremote,
489 NULL,
490 serverlast,
491 &conn);
492 if (result != SASL_OK)
493 saslfail(result, "Allocating sasl connection state", NULL);
494
495 atexit(&free_conn);
496
497 if(extssf) {
498 result = sasl_setprop(conn,
499 SASL_SSF_EXTERNAL,
500 &extssf);
501
502 if (result != SASL_OK)
503 saslfail(result, "Setting external SSF", NULL);
504 }
505
506 if(ext_authid) {
507 result = sasl_setprop(conn,
508 SASL_AUTH_EXTERNAL,
509 &ext_authid);
510
511 if (result != SASL_OK)
512 saslfail(result, "Setting external authid", NULL);
513 }
514
515 result = sasl_setprop(conn,
516 SASL_SEC_PROPS,
517 &secprops);
518
519 if (result != SASL_OK)
520 saslfail(result, "Setting security properties", NULL);
521
522 if (mech) {
523 printf("Forcing use of mechanism %s\n", mech);
524 data = strdup(mech);
525 if (! data)
526 osfail();
527 len = (unsigned) strlen(data);
528 count = 1;
529 } else {
530 puts("Generating client mechanism list...");
531 result = sasl_listmech(conn,
532 ext_authid,
533 NULL,
534 " ",
535 NULL,
536 &data,
537 &len,
538 &count);
539 if (result != SASL_OK)
540 saslfail(result, "Generating client mechanism list", NULL);
541 }
542
543 printf("Sending list of %d mechanism(s)\n", count);
544 samp_send(data, len);
545
546 if(mech) {
547 free((void *)data);
548 }
549
550 puts("Waiting for client mechanism...");
551 len = samp_recv();
552 if (mech && strcasecmp(mech, buf))
553 fail("Client chose something other than the mandatory mechanism");
554 if (strlen(buf) < len) {
555 /* Hmm, there's an initial response here */
556 data = buf + strlen(buf) + 1;
557 len = len - (unsigned) strlen(buf) - 1;
558 } else {
559 data = NULL;
560 len = 0;
561 }
562 result = sasl_server_start(conn,
563 buf,
564 data,
565 len,
566 &data,
567 &len);
568 if (result != SASL_OK && result != SASL_CONTINUE)
569 saslfail(result, "Starting SASL negotiation", sasl_errstring(result,NULL,NULL));
570
571 while (result == SASL_CONTINUE) {
572 if (data) {
573 puts("Sending response...");
574 samp_send(data, len);
575 } else
576 fail("No data to send--something's wrong");
577 puts("Waiting for client reply...");
578 len = samp_recv();
579 data = NULL;
580 result = sasl_server_step(conn, buf, len,
581 &data, &len);
582 if (result != SASL_OK && result != SASL_CONTINUE)
583 saslfail(result, "Performing SASL negotiation", sasl_errstring(result,NULL,NULL));
584 }
585 puts("Negotiation complete");
586
587 if(serverlast&&data) {
588 printf("might need additional send:\n");
589 samp_send(data,len);
590 }
591
592 result = sasl_getprop(conn, SASL_USERNAME, (const void **)&data);
593 if (result != SASL_OK)
594 sasldebug(result, "username", NULL);
595 else
596 printf("Username: %s\n", data ? data : "(NULL)");
597
598 result = sasl_getprop(conn, SASL_DEFUSERREALM, (const void **)&data);
599 if (result != SASL_OK)
600 sasldebug(result, "realm", NULL);
601 else
602 printf("Realm: %s\n", data ? data : "(NULL)");
603
604 result = sasl_getprop(conn, SASL_SSF, (const void **)&ssf);
605 if (result != SASL_OK)
606 sasldebug(result, "ssf", NULL);
607 else
608 printf("SSF: %d\n", *ssf);
609 #define CLIENT_MSG1 "client message 1"
610 #define SERVER_MSG1 "srv message 1"
611 result=sasl_encode(conn,SERVER_MSG1,sizeof(SERVER_MSG1),
612 &data,&len);
613 if (result != SASL_OK)
614 saslfail(result, "sasl_encode", NULL);
615 printf("sending encrypted message '%s'\n",SERVER_MSG1);
616 samp_send(data,len);
617 printf("Waiting for encrypted message...\n");
618 len=samp_recv();
619 {
620 unsigned int recv_len;
621 const char *recv_data;
622 result=sasl_decode(conn,buf,len,&recv_data,&recv_len);
623 if (result != SASL_OK)
624 saslfail(result, "sasl_encode", NULL);
625 printf("recieved decoded message '%s'\n",recv_data);
626 if(strcmp(recv_data,CLIENT_MSG1)!=0)
627 saslfail(1,"recive decoded server message",NULL);
628 }
629
630 #ifdef WIN32
631 WSACleanup();
632 #endif
633
634 return (EXIT_SUCCESS);
635 }
636