2d235e06e5317e6e64458df29aa25785983d3764
[cyrus-sasl.git] / sample / client.c
1 /* $Id: client.c,v 1.7 2004/03/09 17:35:32 rjs3 Exp $ */
2 /* 
3  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer. 
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. The name "Carnegie Mellon University" must not be used to
18  *    endorse or promote products derived from this software without
19  *    prior written permission. For permission or any other legal
20  *    details, please contact  
21  *      Office of Technology Transfer
22  *      Carnegie Mellon University
23  *      5000 Forbes Avenue
24  *      Pittsburgh, PA  15213-3890
25  *      (412) 268-4387, fax: (412) 268-7395
26  *      tech-transfer@andrew.cmu.edu
27  *
28  * 4. Redistributions of any form whatsoever must retain the following
29  *    acknowledgment:
30  *    "This product includes software developed by Computing Services
31  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
32  *
33  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
34  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
35  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
36  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
37  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
38  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
39  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
40  */
41
42 #include <config.h>
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <stdarg.h>
47 #include <ctype.h>
48 #include <errno.h>
49 #include <string.h>
50
51 #ifdef HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <netdb.h>
59
60 #include <assert.h>
61
62 #include <sasl.h>
63
64 #include "common.h"
65
66 /* remove \r\n at end of the line */
67 static void chop(char *s)
68 {
69     char *p;
70
71     assert(s);
72     p = s + strlen(s) - 1;
73     if (p[0] == '\n') {
74         *p-- = '\0';
75     }
76     if (p >= s && p[0] == '\r') {
77         *p-- = '\0';
78     }
79 }
80
81 static int getrealm(void *context __attribute__((unused)), 
82                     int id,
83                     const char **availrealms,
84                     const char **result)
85 {
86     static char buf[1024];
87
88     /* paranoia check */
89     if (id != SASL_CB_GETREALM) return SASL_BADPARAM;
90     if (!result) return SASL_BADPARAM;
91
92     printf("please choose a realm (available:");
93     while (*availrealms) {
94         printf(" %s", *availrealms);
95         availrealms++;
96     }
97     printf("): ");
98
99     fgets(buf, sizeof buf, stdin);
100     chop(buf);
101     *result = buf;
102   
103     return SASL_OK;
104 }
105
106 static int simple(void *context __attribute__((unused)),
107                   int id,
108                   const char **result,
109                   unsigned *len)
110 {
111     static char bufU[1024];
112     static char bufA[1024];
113     char *b;
114
115     /* paranoia check */
116     if (! result)
117         return SASL_BADPARAM;
118
119     switch (id) {
120     case SASL_CB_USER:
121         printf("please enter an authorization id: ");
122         b = bufU;
123         break;
124     case SASL_CB_AUTHNAME:
125         printf("please enter an authentication id: ");
126         b = bufA;
127         break;
128     default:
129         return SASL_BADPARAM;
130     }
131
132     fgets(b, 1024, stdin);
133     chop(b);
134     *result = b;
135     if (len) *len = strlen(b);
136   
137     return SASL_OK;
138 }
139
140 #ifndef HAVE_GETPASSPHRASE
141 static char *
142 getpassphrase(const char *prompt)
143 {
144   return getpass(prompt);
145 }
146 #endif /* ! HAVE_GETPASSPHRASE */
147
148 static int
149 getsecret(sasl_conn_t *conn,
150           void *context __attribute__((unused)),
151           int id,
152           sasl_secret_t **psecret)
153 {
154     char *password;
155     size_t len;
156     static sasl_secret_t *x;
157
158     /* paranoia check */
159     if (! conn || ! psecret || id != SASL_CB_PASS)
160         return SASL_BADPARAM;
161
162     password = getpassphrase("Password: ");
163     if (! password)
164         return SASL_FAIL;
165
166     len = strlen(password);
167
168     x = (sasl_secret_t *) realloc(x, sizeof(sasl_secret_t) + len);
169   
170     if (!x) {
171         memset(password, 0, len);
172         return SASL_NOMEM;
173     }
174
175     x->len = len;
176     strcpy(x->data, password);
177     memset(password, 0, len);
178     
179     *psecret = x;
180     return SASL_OK;
181 }
182
183
184 /* callbacks we support */
185 static sasl_callback_t callbacks[] = {
186   {
187     SASL_CB_GETREALM, &getrealm, NULL
188   }, {
189     SASL_CB_USER, &simple, NULL
190   }, {
191     SASL_CB_AUTHNAME, &simple, NULL
192   }, {
193     SASL_CB_PASS, &getsecret, NULL
194   }, {
195     SASL_CB_LIST_END, NULL, NULL
196   }
197 };
198
199 int getconn(const char *host, const char *port)
200 {
201     struct addrinfo hints, *ai, *r;
202     int err, sock = -1;
203
204     memset(&hints, 0, sizeof(hints));
205     hints.ai_family = PF_UNSPEC;
206     hints.ai_socktype = SOCK_STREAM;
207
208     if ((err = getaddrinfo(host, port, &hints, &ai)) != 0) {
209         fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
210         exit(EX_UNAVAILABLE);
211     }
212
213     for (r = ai; r; r = r->ai_next) {
214         sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
215         if (sock < 0)
216             continue;
217         if (connect(sock, r->ai_addr, r->ai_addrlen) >= 0)
218             break;
219         close(sock);
220         sock = -1;
221     }
222
223     freeaddrinfo(ai);
224     if (sock < 0) {
225         perror("connect");
226         exit(EX_UNAVAILABLE);
227     }
228
229     return sock;
230 }
231
232 char *mech;
233
234 int mysasl_negotiate(FILE *in, FILE *out, sasl_conn_t *conn)
235 {
236     char buf[8192];
237     const char *data;
238     const char *chosenmech;
239     int len;
240     int r, c;
241
242     /* get the capability list */
243     dprintf(0, "receiving capability list... ");
244     len = recv_string(in, buf, sizeof buf);
245     dprintf(0, "%s\n", buf);
246
247     if (mech) {
248         /* make sure that 'mech' appears in 'buf' */
249         if (!strstr(buf, mech)) {
250             printf("server doesn't offer mandatory mech '%s'\n", mech);
251             return -1;
252         }
253     } else {
254         mech = buf;
255     }
256
257     r = sasl_client_start(conn, mech, NULL, &data, &len, &chosenmech);
258     if (r != SASL_OK && r != SASL_CONTINUE) {
259         saslerr(r, "starting SASL negotiation");
260         printf("\n%s\n", sasl_errdetail(conn));
261         return -1;
262     }
263     
264     dprintf(1, "using mechanism %s\n", chosenmech);
265
266     /* we send up to 3 strings;
267        the mechanism chosen, the presence of initial response,
268        and optionally the initial response */
269     send_string(out, chosenmech, strlen(chosenmech));
270     if(data) {
271         send_string(out, "Y", 1);
272         send_string(out, data, len);
273     } else {
274         send_string(out, "N", 1);
275     }
276
277     for (;;) {
278         dprintf(2, "waiting for server reply...\n");
279
280         c = fgetc(in);
281         switch (c) {
282         case 'O':
283             goto done_ok;
284
285         case 'N':
286             goto done_no;
287
288         case 'C': /* continue authentication */
289             break;
290
291         default:
292             printf("bad protocol from server (%c %x)\n", c, c);
293             return -1;
294         }
295         len = recv_string(in, buf, sizeof buf);
296
297         r = sasl_client_step(conn, buf, len, NULL, &data, &len);
298         if (r != SASL_OK && r != SASL_CONTINUE) {
299             saslerr(r, "performing SASL negotiation");
300             printf("\n%s\n", sasl_errdetail(conn));
301             return -1;
302         }
303
304         if (data) {
305             dprintf(2, "sending response length %d...\n", len);
306             send_string(out, data, len);
307         } else {
308             dprintf(2, "sending null response...\n");
309             send_string(out, "", 0);
310         }
311     }
312
313  done_ok:
314     printf("successful authentication\n");
315     return 0;
316
317  done_no:
318     printf("authentication failed\n");
319     return -1;
320 }
321
322 void usage(void)
323 {
324     fprintf(stderr, "usage: client [-p port] [-s service] [-m mech] host\n");
325     exit(EX_USAGE);
326 }
327
328 int main(int argc, char *argv[])
329 {
330     int c;
331     char *host = "localhost";
332     char *port = "12345";
333     char localaddr[NI_MAXHOST + NI_MAXSERV],
334         remoteaddr[NI_MAXHOST + NI_MAXSERV];
335     char *service = "rcmd";
336     char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
337     int r;
338     sasl_conn_t *conn;
339     FILE *in, *out;
340     int fd;
341     int salen;
342     int niflags, error;
343     struct sockaddr_storage local_ip, remote_ip;
344     sasl_channel_bindings cb;
345
346     while ((c = getopt(argc, argv, "p:s:m:")) != EOF) {
347         switch(c) {
348         case 'p':
349             port = optarg;
350             break;
351
352         case 's':
353             service = optarg;
354             break;
355
356         case 'm':
357             mech = optarg;
358             break;
359
360         default:
361             usage();
362             break;
363         }
364     }
365
366     if (optind > argc - 1) {
367         usage();
368     }
369     if (optind == argc - 1) {
370         host = argv[optind];
371     }
372
373     /* initialize the sasl library */
374     r = sasl_client_init(callbacks);
375     if (r != SASL_OK) saslfail(r, "initializing libsasl");
376
377     /* connect to remote server */
378     fd = getconn(host, port);
379
380     /* set ip addresses */
381     salen = sizeof(local_ip);
382     if (getsockname(fd, (struct sockaddr *)&local_ip, &salen) < 0) {
383         perror("getsockname");
384     }
385
386     niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
387 #ifdef NI_WITHSCOPEID
388     if (((struct sockaddr *)&local_ip)->sa_family == AF_INET6)
389       niflags |= NI_WITHSCOPEID;
390 #endif
391     error = getnameinfo((struct sockaddr *)&local_ip, salen,
392                         hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), niflags);
393     if (error != 0) {
394         fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error));
395         strcpy(hbuf, "unknown");
396         strcpy(pbuf, "unknown");
397     }
398     snprintf(localaddr, sizeof(localaddr), "%s;%s", hbuf, pbuf);
399
400     salen = sizeof(remote_ip);
401     if (getpeername(fd, (struct sockaddr *)&remote_ip, &salen) < 0) {
402         perror("getpeername");
403     }
404
405     niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
406 #ifdef NI_WITHSCOPEID
407     if (((struct sockaddr *)&remote_ip)->sa_family == AF_INET6)
408         niflags |= NI_WITHSCOPEID;
409 #endif
410     error = getnameinfo((struct sockaddr *)&remote_ip, salen,
411                         hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), niflags);
412     if (error != 0) {
413         fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error));
414         strcpy(hbuf, "unknown");
415         strcpy(pbuf, "unknown");
416     }
417     snprintf(remoteaddr, sizeof(remoteaddr), "%s;%s", hbuf, pbuf);
418     
419     /* client new connection */
420     r = sasl_client_new(service, host, localaddr, remoteaddr, NULL, 0, &conn);
421     if (r != SASL_OK) saslfail(r, "allocating connection state");
422
423     cb.type = "sasl-sample";
424     cb.critical = 1;
425     cb.data = "this is a test of channel bindings";
426     cb.len = strlen(cb.data);
427
428     sasl_setprop(conn, SASL_CHANNEL_BINDINGS, &cb);
429
430     /* set external properties here
431        sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops); */
432
433     /* set required security properties here
434        sasl_setprop(conn, SASL_SEC_PROPS, &secprops); */
435
436
437     in = fdopen(fd, "r");
438     out = fdopen(fd, "w");
439
440     r = mysasl_negotiate(in, out, conn);
441     if (r == SASL_OK) {
442         /* send/receive data */
443         
444         
445     }
446     
447     printf("closing connection\n");
448     fclose(in);
449     fclose(out);
450     close(fd);
451     sasl_dispose(&conn);
452
453     sasl_done();
454
455     return r;
456 }