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