GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / sample / sample-server.c
1 /* sample-server.c -- sample SASL server
2  * Rob Earhart
3  * $Id: sample-server.c,v 1.31 2004/10/26 11:14:34 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 <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
69 int getsubopt(char **optionp, const char * const *tokens, char **valuep);
70 #endif
71
72 static const char
73 build_ident[] = "$Build: sample-server " PACKAGE "-" VERSION " $";
74
75 static const char *progname = NULL;
76 static int verbose;
77
78 /* Note: if this is changed, change it in samp_read(), too. */
79 #define SAMPLE_SEC_BUF_SIZE (2048)
80
81 static const char
82 message[] = "Come here Watson, I want you.";
83
84 char buf[SAMPLE_SEC_BUF_SIZE];
85
86 static const char *bit_subopts[] = {
87 #define OPT_MIN (0)
88   "min",
89 #define OPT_MAX (1)
90   "max",
91   NULL
92 };
93
94 static const char *ext_subopts[] = {
95 #define OPT_EXT_SSF (0)
96   "ssf",
97 #define OPT_EXT_ID (1)
98   "id",
99   NULL
100 };
101
102 static 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
118 static const char *ip_subopts[] = {
119 #define OPT_IP_LOCAL (0)
120   "local",
121 #define OPT_IP_REMOTE (1)
122   "remote",
123   NULL
124 };
125
126 char *mech = NULL,
127   *iplocal = NULL,
128   *ipremote = NULL,
129   *searchpath = NULL,
130   *service = "rcmd",
131   *localdomain = NULL,
132   *userdomain = NULL;
133 sasl_conn_t *conn = NULL;
134
135 static void
136 free_conn(void)
137 {
138   if (conn)
139     sasl_dispose(&conn);
140 }
141
142 static int
143 sasl_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
170 static int
171 getpath(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
186 static 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
196 static void
197 sasldebug(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
209 static void
210 saslfail(int why, const char *what, const char *errstr)
211 {
212   sasldebug(why, what, errstr);
213   exit(EXIT_FAILURE);
214 }
215
216 static void
217 fail(const char *what)
218 {
219   fprintf(stderr, "%s: %s\n",
220           progname, what);
221   exit(EXIT_FAILURE);
222 }
223
224 static void
225 osfail()
226 {
227   perror(progname);
228   exit(EXIT_FAILURE);
229 }
230
231 static void
232 samp_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
251 static unsigned
252 samp_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
273 int
274 main(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