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