1/* sample-client.c -- sample SASL client
2 * Rob Earhart
3 * $Id: sample-client.c,v 1.33 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#include "../../cyrus_sasl/config.h"
45#include <limits.h>
46#include <stdio.h>
47#include <string.h>
48#include <stdlib.h>
49#ifdef WIN32
50# include <winsock2.h>
51__declspec(dllimport) char *optarg;
52__declspec(dllimport) int optind;
53__declspec(dllimport) int getsubopt(char **optionp, const char * const *tokens, char **valuep);
54#else  /* WIN32 */
55# include <netinet/in.h>
56#endif /* WIN32 */
57#ifdef __APPLE__
58#include <sasl/sasl.h>
59#include <sasl/saslplug.h>
60#include <sasl/saslutil.h>
61#else
62#include <sasl.h>
63#include <saslplug.h>
64#include <saslutil.h>
65#endif
66
67#ifdef macintosh
68#include <sioux.h>
69#include <parse_cmd_line.h>
70#define MAX_ARGC (100)
71int xxx_main(int argc, char *argv[]);
72int main(void)
73{
74	char *argv[MAX_ARGC];
75	int argc;
76	char line[400];
77	SIOUXSettings.asktosaveonclose = 0;
78	SIOUXSettings.showstatusline = 1;
79	argc=parse_cmd_line(MAX_ARGC,argv,sizeof(line),line);
80	return xxx_main(argc,argv);
81}
82#define main xxx_main
83#endif
84
85#ifdef HAVE_GETOPT_H
86#include <getopt.h>
87#endif
88#ifdef HAVE_UNISTD_H
89#include <unistd.h>
90#endif
91
92#ifndef HAVE_GETSUBOPT
93int getsubopt(char **optionp, const char * const *tokens, char **valuep);
94#endif
95
96static const char
97build_ident[] = "$Build: sample-client " PACKAGE "-" VERSION " $";
98
99static const char *progname = NULL;
100static int verbose;
101
102#define SAMPLE_SEC_BUF_SIZE (2048)
103
104#define N_CALLBACKS (16)
105
106static const char
107message[] = "Come here Watson, I want you.";
108
109char buf[SAMPLE_SEC_BUF_SIZE];
110
111static const char *bit_subopts[] = {
112#define OPT_MIN (0)
113  "min",
114#define OPT_MAX (1)
115  "max",
116  NULL
117};
118
119static const char *ext_subopts[] = {
120#define OPT_EXT_SSF (0)
121  "ssf",
122#define OPT_EXT_ID (1)
123  "id",
124  NULL
125};
126
127static const char *flag_subopts[] = {
128#define OPT_NOPLAIN (0)
129  "noplain",
130#define OPT_NOACTIVE (1)
131  "noactive",
132#define OPT_NODICT (2)
133  "nodict",
134#define OPT_FORWARDSEC (3)
135  "forwardsec",
136#define OPT_NOANONYMOUS (4)
137  "noanonymous",
138#define OPT_PASSCRED (5)
139  "passcred",
140  NULL
141};
142
143static const char *ip_subopts[] = {
144#define OPT_IP_LOCAL (0)
145  "local",
146#define OPT_IP_REMOTE (1)
147  "remote",
148  NULL
149};
150
151static sasl_conn_t *conn = NULL;
152
153static void
154free_conn(void)
155{
156  if (conn)
157    sasl_dispose(&conn);
158}
159
160static int
161sasl_my_log(void *context __attribute__((unused)),
162	    int priority,
163	    const char *message)
164{
165  const char *label;
166
167  if (! message)
168    return SASL_BADPARAM;
169
170  switch (priority) {
171  case SASL_LOG_ERR:
172    label = "Error";
173    break;
174  case SASL_LOG_NOTE:
175    label = "Info";
176    break;
177  default:
178    label = "Other";
179    break;
180  }
181
182  fprintf(stderr, "%s: SASL %s: %s\n",
183	  progname, label, message);
184
185  return SASL_OK;
186}
187
188static int getrealm(void *context,
189		    int id,
190		    const char **availrealms __attribute__((unused)),
191		    const char **result)
192{
193  if (id!=SASL_CB_GETREALM) return SASL_FAIL;
194
195  *result=(char *) context;
196
197  return SASL_OK;
198}
199
200static int
201getpath(void *context,
202	const char ** path)
203{
204  const char *searchpath = (const char *) context;
205
206  if (! path)
207    return SASL_BADPARAM;
208
209  if (searchpath) {
210      *path = searchpath;
211  } else {
212      *path = PLUGINDIR;
213  }
214
215  return SASL_OK;
216}
217
218static int
219simple(void *context,
220       int id,
221       const char **result,
222       unsigned *len)
223{
224  const char *value = (const char *)context;
225
226  if (! result)
227    return SASL_BADPARAM;
228
229  switch (id) {
230  case SASL_CB_USER:
231  case SASL_CB_AUTHNAME:
232  case SASL_CB_AUTHN_PRSID:
233  case SASL_CB_AUTHZ_PRSID:
234  case SASL_CB_ATOKEN_TOKEN:
235  case SASL_CB_CLIENTTOKEN_TOKEN:
236    *result = value;
237    if (len)
238      *len = value ? (unsigned) strlen(value) : 0;
239    break;
240  case SASL_CB_LANGUAGE:
241    *result = NULL;
242    if (len)
243      *len = 0;
244    break;
245  default:
246    return SASL_BADPARAM;
247  }
248
249  printf("returning OK: %s\n", *result);
250
251  return SASL_OK;
252}
253
254#ifndef HAVE_GETPASSPHRASE
255static char *
256getpassphrase(const char *prompt)
257{
258  return getpass(prompt);
259}
260#endif /* ! HAVE_GETPASSPHRASE */
261
262static int
263getsecret(sasl_conn_t *conn,
264	  void *context __attribute__((unused)),
265	  int id,
266	  sasl_secret_t **psecret)
267{
268  char *password;
269  unsigned len;
270
271  if (! conn || ! psecret || id != SASL_CB_PASS)
272    return SASL_BADPARAM;
273
274
275  password = getenv("SASL_SAMPLE_CLIENT_PASSWORD");
276  if (! password)
277    password = getpassphrase("Password: ");
278  if (! password)
279    return SASL_FAIL;
280
281  len = (unsigned) strlen(password);
282
283  *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
284
285  if (! *psecret) {
286    memset(password, 0, len);
287    return SASL_NOMEM;
288  }
289
290  (*psecret)->len = len;
291  strcpy((char *)(*psecret)->data, password);
292  memset(password, 0, len);
293
294  return SASL_OK;
295}
296
297static int
298prompt(void *context __attribute__((unused)),
299       int id,
300       const char *challenge,
301       const char *prompt,
302       const char *defresult,
303       const char **result,
304       unsigned *len)
305{
306  if ((id != SASL_CB_ECHOPROMPT && id != SASL_CB_NOECHOPROMPT)
307      || !prompt || !result || !len)
308    return SASL_BADPARAM;
309
310  if (! defresult)
311    defresult = "";
312
313  fputs(prompt, stdout);
314  if (challenge)
315    printf(" [challenge: %s]", challenge);
316  printf(" [%s]: ", defresult);
317  fflush(stdout);
318
319  if (id == SASL_CB_ECHOPROMPT) {
320    char *original = getpassphrase("");
321    if (! original)
322      return SASL_FAIL;
323    if (*original)
324      *result = strdup(original);
325    else
326      *result = strdup(defresult);
327    memset(original, 0L, strlen(original));
328  } else {
329    char buf[1024];
330    fgets(buf, 1024, stdin);
331    if (buf[0]) {
332      *result = strdup(buf);
333    } else {
334      *result = strdup(defresult);
335    }
336    memset(buf, 0L, sizeof(buf));
337  }
338  if (! *result)
339    return SASL_NOMEM;
340
341  *len = (unsigned) strlen(*result);
342
343  return SASL_OK;
344}
345
346static void
347sasldebug(int why, const char *what, const char *errstr)
348{
349  fprintf(stderr, "%s: %s: %s",
350	  progname,
351	  what,
352	  sasl_errstring(why, NULL, NULL));
353  if (errstr)
354    fprintf(stderr, " (%s)\n", errstr);
355  else
356    putc('\n', stderr);
357}
358
359static void
360saslfail(int why, const char *what, const char *errstr)
361{
362  sasldebug(why, what, errstr);
363  free_conn();
364  sasl_done();
365  exit(EXIT_FAILURE);
366}
367
368static void
369fail(const char *what)
370{
371  fprintf(stderr, "%s: %s\n",
372	  progname, what);
373  exit(EXIT_FAILURE);
374}
375
376static void
377osfail()
378{
379  perror(progname);
380  exit(EXIT_FAILURE);
381}
382
383static void
384samp_send(const char *buffer,
385	  unsigned length)
386{
387  char *buf;
388  unsigned len, alloclen;
389  int result;
390
391  alloclen = ((length / 3) + 1) * 4 + 1;
392  buf = malloc(alloclen);
393  if (! buf)
394    osfail();
395  result = sasl_encode64(buffer, length, buf, alloclen, &len);
396  if (result != SASL_OK)
397    saslfail(result, "Encoding data in base64", NULL);
398  printf("C: %s\n", buf);
399  free(buf);
400  fflush(stdout);
401}
402
403static unsigned
404samp_recv()
405{
406  unsigned len;
407  int result;
408
409  if (! fgets(buf, SAMPLE_SEC_BUF_SIZE, stdin)) {
410    fail("Unable to parse input");
411  }
412
413  if (strncmp(buf, "S: ", 3) != 0) {
414    fail("Line must start with 'S: '");
415  }
416
417  len = strlen(buf);
418  if (len > 0 && buf[len-1] == '\n') {
419      buf[len-1] = '\0';
420  }
421
422  result = sasl_decode64(buf + 3, (unsigned) strlen(buf + 3), buf,
423			 SAMPLE_SEC_BUF_SIZE, &len);
424  if (result != SASL_OK)
425    saslfail(result, "Decoding data from base64", NULL);
426  buf[len] = '\0';
427  printf("recieved %d byte message\n",len);
428  if (verbose) { printf("got '%s'\n", buf); }
429  return len;
430}
431
432int
433main(int argc, char *argv[])
434{
435  int c = 0;
436  int errflag = 0;
437  int result;
438  sasl_security_properties_t secprops;
439  sasl_ssf_t extssf = 0;
440  const char *ext_authid = NULL;
441  char *options, *value;
442  const char *data;
443  const char *chosenmech;
444  int serverlast = 0;
445  unsigned len;
446  int clientfirst = 1;
447  sasl_callback_t callbacks[N_CALLBACKS], *callback;
448  char *realm = NULL;
449  char *mech = NULL,
450    *iplocal = NULL,
451    *ipremote = NULL,
452    *searchpath = NULL,
453    *service = "rcmd",
454    *fqdn = "",
455    *userid = NULL,
456    *authid = NULL,
457    *authnPrsid = NULL,
458    *authzPrsid = NULL,
459    *atokenToken = NULL,
460    *clienttokenToken = NULL;
461  sasl_ssf_t *ssf;
462
463#ifdef WIN32
464  /* initialize winsock */
465    WSADATA wsaData;
466
467    result = WSAStartup( MAKEWORD(2, 0), &wsaData );
468    if ( result != 0) {
469	saslfail(SASL_FAIL, "Initializing WinSockets", NULL);
470    }
471#endif
472
473  progname = strrchr(argv[0], HIER_DELIMITER);
474  if (progname)
475    progname++;
476  else
477    progname = argv[0];
478
479  /* Init defaults... */
480  memset(&secprops, 0L, sizeof(secprops));
481  secprops.maxbufsize = SAMPLE_SEC_BUF_SIZE;
482  secprops.max_ssf = UINT_MAX;
483
484  verbose = 0;
485  while ((c = getopt(argc, argv, "vhldb:e:m:f:i:p:r:s:n:u:a:N:Z:T:C:?")) != EOF)
486    switch (c) {
487    case 'v':
488	verbose = 1;
489	break;
490    case 'b':
491      options = optarg;
492      while (*options != '\0')
493	switch(getsubopt(&options, (char * const *)bit_subopts, &value)) {
494	case OPT_MIN:
495	  if (! value)
496	    errflag = 1;
497	  else
498	    secprops.min_ssf = atoi(value);
499	  break;
500	case OPT_MAX:
501	  if (! value)
502	    errflag = 1;
503	  else
504	    secprops.max_ssf = atoi(value);
505	  break;
506	default:
507	  errflag = 1;
508	  break;
509	  }
510      break;
511
512    case 'l':
513	serverlast = SASL_SUCCESS_DATA;
514	break;
515
516    case 'd':
517	clientfirst = 0;
518	break;
519
520    case 'e':
521      options = optarg;
522      while (*options != '\0')
523	switch(getsubopt(&options, (char * const *)ext_subopts, &value)) {
524	case OPT_EXT_SSF:
525	  if (! value)
526	    errflag = 1;
527	  else
528	    extssf = atoi(value);
529	  break;
530	case OPT_MAX:
531	  if (! value)
532	    errflag = 1;
533	  else
534	    ext_authid = value;
535	  break;
536	default:
537	  errflag = 1;
538	  break;
539	  }
540      break;
541
542    case 'm':
543      mech = optarg;
544      break;
545
546    case 'f':
547      options = optarg;
548      while (*options != '\0') {
549	switch(getsubopt(&options, (char * const *)flag_subopts, &value)) {
550	case OPT_NOPLAIN:
551	  secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
552	  break;
553	case OPT_NOACTIVE:
554	  secprops.security_flags |= SASL_SEC_NOACTIVE;
555	  break;
556	case OPT_NODICT:
557	  secprops.security_flags |= SASL_SEC_NODICTIONARY;
558	  break;
559	case OPT_FORWARDSEC:
560	  secprops.security_flags |= SASL_SEC_FORWARD_SECRECY;
561	  break;
562	case OPT_NOANONYMOUS:
563	  secprops.security_flags |= SASL_SEC_NOANONYMOUS;
564	  break;
565	case OPT_PASSCRED:
566	  secprops.security_flags |= SASL_SEC_PASS_CREDENTIALS;
567	  break;
568	default:
569	  errflag = 1;
570	  break;
571	  }
572	if (value) errflag = 1;
573	}
574      break;
575
576    case 'i':
577      options = optarg;
578      while (*options != '\0')
579	switch(getsubopt(&options, (char * const *)ip_subopts, &value)) {
580	case OPT_IP_LOCAL:
581	  if (! value)
582	    errflag = 1;
583	  else
584	    iplocal = value;
585	  break;
586	case OPT_IP_REMOTE:
587	  if (! value)
588	    errflag = 1;
589	  else
590	    ipremote = value;
591	  break;
592	default:
593	  errflag = 1;
594	  break;
595	  }
596      break;
597
598    case 'p':
599      searchpath = optarg;
600      break;
601
602    case 'r':
603      realm = optarg;
604      break;
605
606    case 's':
607      service=malloc(1000);
608      strcpy(service,optarg);
609      /*      service = optarg;*/
610      printf("service=%s\n",service);
611      break;
612
613    case 'n':
614      fqdn = optarg;
615      break;
616
617    case 'u':
618      userid = optarg;
619      break;
620
621    case 'a':
622      authid = optarg;
623      break;
624
625    case 'N':
626      authnPrsid = optarg;
627      break;
628
629    case 'Z':
630      authzPrsid = optarg;
631      break;
632
633    case 'T':
634      atokenToken = optarg;
635      break;
636
637    case 'C':
638      clienttokenToken = optarg;
639      break;
640
641    default:			/* unknown flag */
642      errflag = 1;
643      break;
644    }
645
646  if (optind != argc) {
647    /* We don't *have* extra arguments */
648    errflag = 1;
649  }
650
651  if (errflag) {
652    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] [-s NAME] [-n FQDN] [-u ID] [-a ID]\n"
653	    "\t-b ...\t#bits to use for encryption\n"
654	    "\t\tmin=N\tminumum #bits to use (1 => integrity)\n"
655	    "\t\tmax=N\tmaximum #bits to use\n"
656	    "\t-e ...\tassume external encryption\n"
657	    "\t\tssf=N\texternal mech provides N bits of encryption\n"
658	    "\t\tid=ID\texternal mech provides authentication id ID\n"
659	    "\t-m MECH\tforce use of MECH for security\n"
660	    "\t-f ...\tset security flags\n"
661	    "\t\tnoplain\t\trequire security vs. passive attacks\n"
662	    "\t\tnoactive\trequire security vs. active attacks\n"
663	    "\t\tnodict\t\trequire security vs. passive dictionary attacks\n"
664	    "\t\tforwardsec\trequire forward secrecy\n"
665	    "\t\tmaximum\t\trequire all security flags\n"
666	    "\t\tpasscred\tattempt to pass client credentials\n"
667	    "\t-i ...\tset IP addresses (required by some mechs)\n"
668	    "\t\tlocal=IP;PORT\tset local address to IP, port PORT\n"
669	    "\t\tremote=IP;PORT\tset remote address to IP, port PORT\n"
670	    "\t-p PATH\tcolon-seperated search path for mechanisms\n"
671	    "\t-r REALM\trealm to use"
672	    "\t-s NAME\tservice name pass to mechanisms\n"
673	    "\t-n FQDN\tserver fully-qualified domain name\n"
674	    "\t-u ID\tuser (authorization) id to request\n"
675	    "\t-a ID\tid to authenticate as\n"
676	    "\t-N ID\tprsid to authenticate as\n"
677	    "\t-Z ID\tprsid to authorize as\n"
678	    "\t-T ID\tauthentication token\n"
679	    "\t-C ID\tclient token\n"
680	    "\t-a ID\tid to authenticate as\n"
681	    "\t-d\tDisable client-send-first\n"
682	    "\t-l\tEnable server-send-last\n",
683	    progname, progname);
684    exit(EXIT_FAILURE);
685  }
686
687  /* Fill in the callbacks that we're providing... */
688  callback = callbacks;
689
690  /* log */
691  callback->id = SASL_CB_LOG;
692  callback->proc = (sasl_callback_ft)&sasl_my_log;
693  callback->context = NULL;
694  ++callback;
695
696  /* getpath */
697  if (searchpath) {
698    callback->id = SASL_CB_GETPATH;
699    callback->proc = (sasl_callback_ft)&getpath;
700    callback->context = searchpath;
701    ++callback;
702  }
703
704  /* user */
705  if (userid) {
706    callback->id = SASL_CB_USER;
707    callback->proc = (sasl_callback_ft)&simple;
708    callback->context = userid;
709    ++callback;
710  }
711
712  /* authname */
713  if (authid) {
714    callback->id = SASL_CB_AUTHNAME;
715    callback->proc = (sasl_callback_ft)&simple;
716    callback->context = authid;
717    ++callback;
718  }
719
720  /* ATOKEN */
721
722  if (authnPrsid) {
723    callback->id = SASL_CB_AUTHN_PRSID;
724    callback->proc = &simple;
725    callback->context = authnPrsid;
726    ++callback;
727  }
728  if (authzPrsid) {
729    callback->id = SASL_CB_AUTHZ_PRSID;
730    callback->proc = &simple;
731    callback->context = authzPrsid;
732    ++callback;
733  }
734  if (atokenToken) {
735    callback->id = SASL_CB_ATOKEN_TOKEN;
736    callback->proc = &simple;
737    callback->context = atokenToken;
738    ++callback;
739  }
740  if (clienttokenToken) {
741    callback->id = SASL_CB_CLIENTTOKEN_TOKEN;
742    callback->proc = &simple;
743    callback->context = clienttokenToken;
744    ++callback;
745  }
746
747
748  if (realm!=NULL)
749  {
750    callback->id = SASL_CB_GETREALM;
751    callback->proc = (sasl_callback_ft)&getrealm;
752    callback->context = realm;
753    callback++;
754  }
755
756  /* password */
757  callback->id = SASL_CB_PASS;
758  callback->proc = (sasl_callback_ft)&getsecret;
759  callback->context = NULL;
760  ++callback;
761
762  /* echoprompt */
763  callback->id = SASL_CB_ECHOPROMPT;
764  callback->proc = (sasl_callback_ft)&prompt;
765  callback->context = NULL;
766  ++callback;
767
768  /* noechoprompt */
769  callback->id = SASL_CB_NOECHOPROMPT;
770  callback->proc = (sasl_callback_ft)&prompt;
771  callback->context = NULL;
772  ++callback;
773
774  /* termination */
775  callback->id = SASL_CB_LIST_END;
776  callback->proc = NULL;
777  callback->context = NULL;
778  ++callback;
779
780  if (N_CALLBACKS < callback - callbacks)
781    fail("Out of callback space; recompile with larger N_CALLBACKS");
782
783  result = sasl_client_init(callbacks);
784  if (result != SASL_OK)
785    saslfail(result, "Initializing libsasl", NULL);
786
787  result = sasl_client_new(service,
788			   fqdn,
789			   iplocal,ipremote,
790			   NULL,serverlast,
791			   &conn);
792  if (result != SASL_OK)
793    saslfail(result, "Allocating sasl connection state", NULL);
794
795  if(extssf) {
796      result = sasl_setprop(conn,
797			    SASL_SSF_EXTERNAL,
798			    &extssf);
799
800      if (result != SASL_OK)
801	  saslfail(result, "Setting external SSF", NULL);
802  }
803
804  if(ext_authid) {
805      result = sasl_setprop(conn,
806			    SASL_AUTH_EXTERNAL,
807			    &ext_authid);
808
809      if (result != SASL_OK)
810	  saslfail(result, "Setting external authid", NULL);
811  }
812
813  result = sasl_setprop(conn,
814			SASL_SEC_PROPS,
815			&secprops);
816
817  if (result != SASL_OK)
818    saslfail(result, "Setting security properties", NULL);
819
820  puts("Waiting for mechanism list from server...");
821  len = samp_recv();
822
823  if (mech) {
824    printf("Forcing use of mechanism %s\n", mech);
825    strncpy(buf, mech, SAMPLE_SEC_BUF_SIZE);
826    buf[SAMPLE_SEC_BUF_SIZE - 1] = '\0';
827  }
828
829  printf("Choosing best mechanism from: %s\n", buf);
830
831  if(clientfirst) {
832      result = sasl_client_start(conn,
833				 buf,
834				 NULL,
835				 &data,
836				 &len,
837				 &chosenmech);
838  } else {
839      data = "";
840      len = 0;
841      result = sasl_client_start(conn,
842				 buf,
843				 NULL,
844				 NULL,
845				 0,
846				 &chosenmech);
847  }
848
849
850  if (result != SASL_OK && result != SASL_CONTINUE) {
851      printf("error was %s\n", sasl_errdetail(conn));
852      saslfail(result, "Starting SASL negotiation", NULL);
853  }
854
855  printf("Using mechanism %s\n", chosenmech);
856  strcpy(buf, chosenmech);
857  if (data) {
858    if (SAMPLE_SEC_BUF_SIZE - strlen(buf) - 1 < len)
859      fail("Not enough buffer space");
860    puts("Preparing initial.");
861    memcpy(buf + strlen(buf) + 1, data, len);
862    len += (unsigned) strlen(buf) + 1;
863    data = NULL;
864  } else {
865    len = (unsigned) strlen(buf);
866  }
867
868  puts("Sending initial response...");
869  samp_send(buf, len);
870
871  while (result == SASL_CONTINUE) {
872    puts("Waiting for server reply...");
873    len = samp_recv();
874    result = sasl_client_step(conn, buf, len, NULL,
875			      &data, &len);
876    if (result != SASL_OK && result != SASL_CONTINUE)
877	saslfail(result, "Performing SASL negotiation", NULL);
878    if (data && len) {
879	puts("Sending response...");
880	samp_send(data, len);
881    } else if (result != SASL_OK || !serverlast) {
882	samp_send("",0);
883    }
884
885  }
886  puts("Negotiation complete");
887
888  result = sasl_getprop(conn, SASL_USERNAME, (const void **)&data);
889  if (result != SASL_OK)
890    sasldebug(result, "username", NULL);
891  else
892    printf("Username: %s\n", data);
893
894#define CLIENT_MSG1 "client message 1"
895#define SERVER_MSG1 "srv message 1"
896
897  result = sasl_getprop(conn, SASL_SSF, (const void **)&ssf);
898  if (result != SASL_OK)
899    sasldebug(result, "ssf", NULL);
900  else
901    printf("SSF: %d\n", *ssf);
902
903 printf("Waiting for encoded message...\n");
904 len=samp_recv();
905 {
906 	unsigned int recv_len;
907 	const char *recv_data;
908	result=sasl_decode(conn,buf,len,&recv_data,&recv_len);
909 	if (result != SASL_OK)
910	    saslfail(result, "sasl_decode", NULL);
911	printf("recieved decoded message '%s'\n",recv_data);
912	if(strcmp(recv_data,SERVER_MSG1)!=0)
913	    saslfail(1,"recive decoded server message",NULL);
914 }
915  result=sasl_encode(conn,CLIENT_MSG1,sizeof(CLIENT_MSG1),
916  	&data,&len);
917  if (result != SASL_OK)
918      saslfail(result, "sasl_encode", NULL);
919  printf("sending encrypted message '%s'\n",CLIENT_MSG1);
920  samp_send(data,len);
921
922  free_conn();
923  sasl_done();
924
925#ifdef WIN32
926  WSACleanup();
927#endif
928  return (EXIT_SUCCESS);
929}
930