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