1 /* sample-client.c -- sample SASL client
3 * $Id: sample-client.c,v 1.31 2004/10/26 11:14:33 mel Exp $
6 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
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
27 * Pittsburgh, PA 15213-3890
28 * (412) 268-4387, fax: (412) 268-7395
29 * tech-transfer@andrew.cmu.edu
31 * 4. Redistributions of any form whatsoever must retain the following
33 * "This product includes software developed by Computing Services
34 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
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.
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);
55 # include <netinet/in.h>
62 #include <parse_cmd_line.h>
63 #define MAX_ARGC (100)
64 int xxx_main(int argc, char *argv[]);
70 SIOUXSettings.asktosaveonclose = 0;
71 SIOUXSettings.showstatusline = 1;
72 argc=parse_cmd_line(MAX_ARGC,argv,sizeof(line),line);
73 return xxx_main(argc,argv);
85 #ifndef HAVE_GETSUBOPT
86 int getsubopt(char **optionp, const char * const *tokens, char **valuep);
90 build_ident[] = "$Build: sample-client " PACKAGE "-" VERSION " $";
92 static const char *progname = NULL;
95 #define SAMPLE_SEC_BUF_SIZE (2048)
97 #define N_CALLBACKS (16)
100 message[] = "Come here Watson, I want you.";
102 char buf[SAMPLE_SEC_BUF_SIZE];
104 static const char *bit_subopts[] = {
112 static const char *ext_subopts[] = {
113 #define OPT_EXT_SSF (0)
115 #define OPT_EXT_ID (1)
120 static const char *flag_subopts[] = {
121 #define OPT_NOPLAIN (0)
123 #define OPT_NOACTIVE (1)
125 #define OPT_NODICT (2)
127 #define OPT_FORWARDSEC (3)
129 #define OPT_NOANONYMOUS (4)
131 #define OPT_PASSCRED (5)
136 static const char *ip_subopts[] = {
137 #define OPT_IP_LOCAL (0)
139 #define OPT_IP_REMOTE (1)
144 static sasl_conn_t *conn = NULL;
154 sasl_my_log(void *context __attribute__((unused)),
161 return SASL_BADPARAM;
175 fprintf(stderr, "%s: SASL %s: %s\n",
176 progname, label, message);
181 static int getrealm(void *context,
183 const char **availrealms __attribute__((unused)),
186 if (id!=SASL_CB_GETREALM) return SASL_FAIL;
188 *result=(char *) context;
194 getpath(void *context,
197 const char *searchpath = (const char *) context;
200 return SASL_BADPARAM;
212 simple(void *context,
217 const char *value = (const char *)context;
220 return SASL_BADPARAM;
226 *len = value ? (unsigned) strlen(value) : 0;
228 case SASL_CB_AUTHNAME:
231 *len = value ? (unsigned) strlen(value) : 0;
233 case SASL_CB_LANGUAGE:
239 return SASL_BADPARAM;
242 printf("returning OK: %s\n", *result);
247 #ifndef HAVE_GETPASSPHRASE
249 getpassphrase(const char *prompt)
251 return getpass(prompt);
253 #endif /* ! HAVE_GETPASSPHRASE */
256 getsecret(sasl_conn_t *conn,
257 void *context __attribute__((unused)),
259 sasl_secret_t **psecret)
264 if (! conn || ! psecret || id != SASL_CB_PASS)
265 return SASL_BADPARAM;
267 password = getpassphrase("Password: ");
271 len = (unsigned) strlen(password);
273 *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
276 memset(password, 0, len);
280 (*psecret)->len = len;
281 strcpy((char *)(*psecret)->data, password);
282 memset(password, 0, len);
288 prompt(void *context __attribute__((unused)),
290 const char *challenge,
292 const char *defresult,
296 if ((id != SASL_CB_ECHOPROMPT && id != SASL_CB_NOECHOPROMPT)
297 || !prompt || !result || !len)
298 return SASL_BADPARAM;
303 fputs(prompt, stdout);
305 printf(" [challenge: %s]", challenge);
306 printf(" [%s]: ", defresult);
309 if (id == SASL_CB_ECHOPROMPT) {
310 char *original = getpassphrase("");
314 *result = strdup(original);
316 *result = strdup(defresult);
317 memset(original, 0L, strlen(original));
320 fgets(buf, 1024, stdin);
322 *result = strdup(buf);
324 *result = strdup(defresult);
326 memset(buf, 0L, sizeof(buf));
331 *len = (unsigned) strlen(*result);
337 sasldebug(int why, const char *what, const char *errstr)
339 fprintf(stderr, "%s: %s: %s",
342 sasl_errstring(why, NULL, NULL));
344 fprintf(stderr, " (%s)\n", errstr);
350 saslfail(int why, const char *what, const char *errstr)
352 sasldebug(why, what, errstr);
359 fail(const char *what)
361 fprintf(stderr, "%s: %s\n",
374 samp_send(const char *buffer,
378 unsigned len, alloclen;
381 alloclen = ((length / 3) + 1) * 4 + 1;
382 buf = malloc(alloclen);
385 result = sasl_encode64(buffer, length, buf, alloclen, &len);
386 if (result != SASL_OK)
387 saslfail(result, "Encoding data in base64", NULL);
388 printf("C: %s\n", buf);
398 if (! fgets(buf, SAMPLE_SEC_BUF_SIZE, stdin)
399 || strncmp(buf, "S: ", 3))
400 fail("Unable to parse input");
401 result = sasl_decode64(buf + 3, (unsigned) strlen(buf + 3), buf,
402 SAMPLE_SEC_BUF_SIZE, &len);
403 if (result != SASL_OK)
404 saslfail(result, "Decoding data from base64", NULL);
406 printf("recieved %d byte message\n",len);
407 if (verbose) { printf("got '%s'\n", buf); }
412 main(int argc, char *argv[])
417 sasl_security_properties_t secprops;
418 sasl_ssf_t extssf = 0;
419 const char *ext_authid = NULL;
420 char *options, *value;
422 const char *chosenmech;
426 sasl_callback_t callbacks[N_CALLBACKS], *callback;
439 /* initialize winsock */
442 result = WSAStartup( MAKEWORD(2, 0), &wsaData );
444 saslfail(SASL_FAIL, "Initializing WinSockets", NULL);
448 progname = strrchr(argv[0], HIER_DELIMITER);
454 /* Init defaults... */
455 memset(&secprops, 0L, sizeof(secprops));
456 secprops.maxbufsize = SAMPLE_SEC_BUF_SIZE;
457 secprops.max_ssf = UINT_MAX;
460 while ((c = getopt(argc, argv, "vhldb:e:m:f:i:p:r:s:n:u:a:?")) != EOF)
467 while (*options != '\0')
468 switch(getsubopt(&options, (const char * const *)bit_subopts, &value)) {
473 secprops.min_ssf = atoi(value);
479 secprops.max_ssf = atoi(value);
488 serverlast = SASL_SUCCESS_DATA;
497 while (*options != '\0')
498 switch(getsubopt(&options, (const char * const *)ext_subopts, &value)) {
503 extssf = atoi(value);
523 while (*options != '\0') {
524 switch(getsubopt(&options, (const char * const *)flag_subopts, &value)) {
526 secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
529 secprops.security_flags |= SASL_SEC_NOACTIVE;
532 secprops.security_flags |= SASL_SEC_NODICTIONARY;
535 secprops.security_flags |= SASL_SEC_FORWARD_SECRECY;
537 case OPT_NOANONYMOUS:
538 secprops.security_flags |= SASL_SEC_NOANONYMOUS;
541 secprops.security_flags |= SASL_SEC_PASS_CREDENTIALS;
547 if (value) errflag = 1;
553 while (*options != '\0')
554 switch(getsubopt(&options, (const char * const *)ip_subopts, &value)) {
582 service=malloc(1000);
583 strcpy(service,optarg);
584 /* service = optarg;*/
585 printf("service=%s\n",service);
600 default: /* unknown flag */
605 if (optind != argc) {
606 /* We don't *have* extra arguments */
611 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"
612 "\t-b ...\t#bits to use for encryption\n"
613 "\t\tmin=N\tminumum #bits to use (1 => integrity)\n"
614 "\t\tmax=N\tmaximum #bits to use\n"
615 "\t-e ...\tassume external encryption\n"
616 "\t\tssf=N\texternal mech provides N bits of encryption\n"
617 "\t\tid=ID\texternal mech provides authentication id ID\n"
618 "\t-m MECH\tforce use of MECH for security\n"
619 "\t-f ...\tset security flags\n"
620 "\t\tnoplain\t\trequire security vs. passive attacks\n"
621 "\t\tnoactive\trequire security vs. active attacks\n"
622 "\t\tnodict\t\trequire security vs. passive dictionary attacks\n"
623 "\t\tforwardsec\trequire forward secrecy\n"
624 "\t\tmaximum\t\trequire all security flags\n"
625 "\t\tpasscred\tattempt to pass client credentials\n"
626 "\t-i ...\tset IP addresses (required by some mechs)\n"
627 "\t\tlocal=IP;PORT\tset local address to IP, port PORT\n"
628 "\t\tremote=IP;PORT\tset remote address to IP, port PORT\n"
629 "\t-p PATH\tcolon-seperated search path for mechanisms\n"
630 "\t-r REALM\trealm to use"
631 "\t-s NAME\tservice name pass to mechanisms\n"
632 "\t-n FQDN\tserver fully-qualified domain name\n"
633 "\t-u ID\tuser (authorization) id to request\n"
634 "\t-a ID\tid to authenticate as\n"
635 "\t-d\tDisable client-send-first\n"
636 "\t-l\tEnable server-send-last\n",
641 /* Fill in the callbacks that we're providing... */
642 callback = callbacks;
645 callback->id = SASL_CB_LOG;
646 callback->proc = &sasl_my_log;
647 callback->context = NULL;
652 callback->id = SASL_CB_GETPATH;
653 callback->proc = &getpath;
654 callback->context = searchpath;
660 callback->id = SASL_CB_USER;
661 callback->proc = &simple;
662 callback->context = userid;
668 callback->id = SASL_CB_AUTHNAME;
669 callback->proc = &simple;
670 callback->context = authid;
676 callback->id = SASL_CB_GETREALM;
677 callback->proc = &getrealm;
678 callback->context = realm;
683 callback->id = SASL_CB_PASS;
684 callback->proc = &getsecret;
685 callback->context = NULL;
689 callback->id = SASL_CB_ECHOPROMPT;
690 callback->proc = &prompt;
691 callback->context = NULL;
695 callback->id = SASL_CB_NOECHOPROMPT;
696 callback->proc = &prompt;
697 callback->context = NULL;
701 callback->id = SASL_CB_LIST_END;
702 callback->proc = NULL;
703 callback->context = NULL;
706 if (N_CALLBACKS < callback - callbacks)
707 fail("Out of callback space; recompile with larger N_CALLBACKS");
709 result = sasl_client_init(callbacks);
710 if (result != SASL_OK)
711 saslfail(result, "Initializing libsasl", NULL);
713 result = sasl_client_new(service,
718 if (result != SASL_OK)
719 saslfail(result, "Allocating sasl connection state", NULL);
722 result = sasl_setprop(conn,
726 if (result != SASL_OK)
727 saslfail(result, "Setting external SSF", NULL);
731 result = sasl_setprop(conn,
735 if (result != SASL_OK)
736 saslfail(result, "Setting external authid", NULL);
739 result = sasl_setprop(conn,
743 if (result != SASL_OK)
744 saslfail(result, "Setting security properties", NULL);
746 puts("Waiting for mechanism list from server...");
750 printf("Forcing use of mechanism %s\n", mech);
751 strncpy(buf, mech, SAMPLE_SEC_BUF_SIZE);
752 buf[SAMPLE_SEC_BUF_SIZE - 1] = '\0';
755 printf("Choosing best mechanism from: %s\n", buf);
758 result = sasl_client_start(conn,
767 result = sasl_client_start(conn,
776 if (result != SASL_OK && result != SASL_CONTINUE) {
777 printf("error was %s\n", sasl_errdetail(conn));
778 saslfail(result, "Starting SASL negotiation", NULL);
781 printf("Using mechanism %s\n", chosenmech);
782 strcpy(buf, chosenmech);
784 if (SAMPLE_SEC_BUF_SIZE - strlen(buf) - 1 < len)
785 fail("Not enough buffer space");
786 puts("Preparing initial.");
787 memcpy(buf + strlen(buf) + 1, data, len);
788 len += (unsigned) strlen(buf) + 1;
791 len = (unsigned) strlen(buf);
794 puts("Sending initial response...");
797 while (result == SASL_CONTINUE) {
798 puts("Waiting for server reply...");
800 result = sasl_client_step(conn, buf, len, NULL,
802 if (result != SASL_OK && result != SASL_CONTINUE)
803 saslfail(result, "Performing SASL negotiation", NULL);
805 puts("Sending response...");
806 samp_send(data, len);
807 } else if (result != SASL_OK || !serverlast) {
812 puts("Negotiation complete");
814 result = sasl_getprop(conn, SASL_USERNAME, (const void **)&data);
815 if (result != SASL_OK)
816 sasldebug(result, "username", NULL);
818 printf("Username: %s\n", data);
820 #define CLIENT_MSG1 "client message 1"
821 #define SERVER_MSG1 "srv message 1"
823 result = sasl_getprop(conn, SASL_SSF, (const void **)&ssf);
824 if (result != SASL_OK)
825 sasldebug(result, "ssf", NULL);
827 printf("SSF: %d\n", *ssf);
829 printf("Waiting for encoded message...\n");
832 unsigned int recv_len;
833 const char *recv_data;
834 result=sasl_decode(conn,buf,len,&recv_data,&recv_len);
835 if (result != SASL_OK)
836 saslfail(result, "sasl_decode", NULL);
837 printf("recieved decoded message '%s'\n",recv_data);
838 if(strcmp(recv_data,SERVER_MSG1)!=0)
839 saslfail(1,"recive decoded server message",NULL);
841 result=sasl_encode(conn,CLIENT_MSG1,sizeof(CLIENT_MSG1),
843 if (result != SASL_OK)
844 saslfail(result, "sasl_encode", NULL);
845 printf("sending encrypted message '%s'\n",CLIENT_MSG1);
854 return (EXIT_SUCCESS);