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