0ddb16c7725c3b7cd60cce6deaa6ddeeffc1ecc4
[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 buf[1024];
112
113     /* paranoia check */
114     if (! result)
115         return SASL_BADPARAM;
116
117     switch (id) {
118     case SASL_CB_USER:
119         printf("please enter an authorization id: ");
120         break;
121     case SASL_CB_AUTHNAME:
122         printf("please enter an authentication id: ");
123         break;
124     default:
125         return SASL_BADPARAM;
126     }
127
128     fgets(buf, sizeof buf, stdin);
129     chop(buf);
130     *result = buf;
131     if (len) *len = strlen(buf);
132   
133     return SASL_OK;
134 }
135
136 #ifndef HAVE_GETPASSPHRASE
137 static char *
138 getpassphrase(const char *prompt)
139 {
140   return getpass(prompt);
141 }
142 #endif /* ! HAVE_GETPASSPHRASE */
143
144 static int
145 getsecret(sasl_conn_t *conn,
146           void *context __attribute__((unused)),
147           int id,
148           sasl_secret_t **psecret)
149 {
150     char *password;
151     size_t len;
152     static sasl_secret_t *x;
153
154     /* paranoia check */
155     if (! conn || ! psecret || id != SASL_CB_PASS)
156         return SASL_BADPARAM;
157
158     password = getpassphrase("Password: ");
159     if (! password)
160         return SASL_FAIL;
161
162     len = strlen(password);
163
164     x = (sasl_secret_t *) realloc(x, sizeof(sasl_secret_t) + len);
165   
166     if (!x) {
167         memset(password, 0, len);
168         return SASL_NOMEM;
169     }
170
171     x->len = len;
172     strcpy(x->data, password);
173     memset(password, 0, len);
174     
175     *psecret = x;
176     return SASL_OK;
177 }
178
179
180 /* callbacks we support */
181 static sasl_callback_t callbacks[] = {
182   {
183     SASL_CB_GETREALM, &getrealm, NULL
184   }, {
185     SASL_CB_USER, &simple, NULL
186   }, {
187     SASL_CB_AUTHNAME, &simple, NULL
188   }, {
189     SASL_CB_PASS, &getsecret, NULL
190   }, {
191     SASL_CB_LIST_END, NULL, NULL
192   }
193 };
194
195 int getconn(const char *host, const char *port)
196 {
197     struct addrinfo hints, *ai, *r;
198     int err, sock = -1;
199
200     memset(&hints, 0, sizeof(hints));
201     hints.ai_family = PF_UNSPEC;
202     hints.ai_socktype = SOCK_STREAM;
203
204     if ((err = getaddrinfo(host, port, &hints, &ai)) != 0) {
205         fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
206         exit(EX_UNAVAILABLE);
207     }
208
209     for (r = ai; r; r = r->ai_next) {
210         sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
211         if (sock < 0)
212             continue;
213         if (connect(sock, r->ai_addr, r->ai_addrlen) >= 0)
214             break;
215         close(sock);
216         sock = -1;
217     }
218
219     freeaddrinfo(ai);
220     if (sock < 0) {
221         perror("connect");
222         exit(EX_UNAVAILABLE);
223     }
224
225     return sock;
226 }
227
228 char *mech;
229
230 int mysasl_negotiate(FILE *in, FILE *out, sasl_conn_t *conn)
231 {
232     char buf[8192];
233     const char *data;
234     const char *chosenmech;
235     int len;
236     int r, c;
237
238     /* get the capability list */
239     dprintf(0, "receiving capability list... ");
240     len = recv_string(in, buf, sizeof buf);
241     dprintf(0, "%s\n", buf);
242
243     if (mech) {
244         /* make sure that 'mech' appears in 'buf' */
245         if (!strstr(buf, mech)) {
246             printf("server doesn't offer mandatory mech '%s'\n", mech);
247             return -1;
248         }
249     } else {
250         mech = buf;
251     }
252
253     r = sasl_client_start(conn, mech, NULL, &data, &len, &chosenmech);
254     if (r != SASL_OK && r != SASL_CONTINUE) {
255         saslerr(r, "starting SASL negotiation");
256         printf("\n%s\n", sasl_errdetail(conn));
257         return -1;
258     }
259     
260     dprintf(1, "using mechanism %s\n", chosenmech);
261
262     /* we send up to 3 strings;
263        the mechanism chosen, the presence of initial response,
264        and optionally the initial response */
265     send_string(out, chosenmech, strlen(chosenmech));
266     if(data) {
267         send_string(out, "Y", 1);
268         send_string(out, data, len);
269     } else {
270         send_string(out, "N", 1);
271     }
272
273     for (;;) {
274         dprintf(2, "waiting for server reply...\n");
275
276         c = fgetc(in);
277         switch (c) {
278         case 'O':
279             goto done_ok;
280
281         case 'N':
282             goto done_no;
283
284         case 'C': /* continue authentication */
285             break;
286
287         default:
288             printf("bad protocol from server (%c %x)\n", c, c);
289             return -1;
290         }
291         len = recv_string(in, buf, sizeof buf);
292
293         r = sasl_client_step(conn, buf, len, NULL, &data, &len);
294         if (r != SASL_OK && r != SASL_CONTINUE) {
295             saslerr(r, "performing SASL negotiation");
296             printf("\n%s\n", sasl_errdetail(conn));
297             return -1;
298         }
299
300         if (data) {
301             dprintf(2, "sending response length %d...\n", len);
302             send_string(out, data, len);
303         } else {
304             dprintf(2, "sending null response...\n");
305             send_string(out, "", 0);
306         }
307     }
308
309  done_ok:
310     printf("successful authentication\n");
311     return 0;
312
313  done_no:
314     printf("authentication failed\n");
315     return -1;
316 }
317
318 void usage(void)
319 {
320     fprintf(stderr, "usage: client [-p port] [-s service] [-m mech] host\n");
321     exit(EX_USAGE);
322 }
323
324 int main(int argc, char *argv[])
325 {
326     int c;
327     char *host = "localhost";
328     char *port = "12345";
329     char localaddr[NI_MAXHOST + NI_MAXSERV],
330         remoteaddr[NI_MAXHOST + NI_MAXSERV];
331     char *service = "rcmd";
332     char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
333     int r;
334     sasl_conn_t *conn;
335     FILE *in, *out;
336     int fd;
337     int salen;
338     int niflags, error;
339     struct sockaddr_storage local_ip, remote_ip;
340
341     while ((c = getopt(argc, argv, "p:s:m:")) != EOF) {
342         switch(c) {
343         case 'p':
344             port = optarg;
345             break;
346
347         case 's':
348             service = optarg;
349             break;
350
351         case 'm':
352             mech = optarg;
353             break;
354
355         default:
356             usage();
357             break;
358         }
359     }
360
361     if (optind > argc - 1) {
362         usage();
363     }
364     if (optind == argc - 1) {
365         host = argv[optind];
366     }
367
368     /* initialize the sasl library */
369     r = sasl_client_init(callbacks);
370     if (r != SASL_OK) saslfail(r, "initializing libsasl");
371
372     /* connect to remote server */
373     fd = getconn(host, port);
374
375     /* set ip addresses */
376     salen = sizeof(local_ip);
377     if (getsockname(fd, (struct sockaddr *)&local_ip, &salen) < 0) {
378         perror("getsockname");
379     }
380
381     niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
382 #ifdef NI_WITHSCOPEID
383     if (((struct sockaddr *)&local_ip)->sa_family == AF_INET6)
384       niflags |= NI_WITHSCOPEID;
385 #endif
386     error = getnameinfo((struct sockaddr *)&local_ip, salen,
387                         hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), niflags);
388     if (error != 0) {
389         fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error));
390         strcpy(hbuf, "unknown");
391         strcpy(pbuf, "unknown");
392     }
393     snprintf(localaddr, sizeof(localaddr), "%s;%s", hbuf, pbuf);
394
395     salen = sizeof(remote_ip);
396     if (getpeername(fd, (struct sockaddr *)&remote_ip, &salen) < 0) {
397         perror("getpeername");
398     }
399
400     niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
401 #ifdef NI_WITHSCOPEID
402     if (((struct sockaddr *)&remote_ip)->sa_family == AF_INET6)
403         niflags |= NI_WITHSCOPEID;
404 #endif
405     error = getnameinfo((struct sockaddr *)&remote_ip, salen,
406                         hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), niflags);
407     if (error != 0) {
408         fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error));
409         strcpy(hbuf, "unknown");
410         strcpy(pbuf, "unknown");
411     }
412     snprintf(remoteaddr, sizeof(remoteaddr), "%s;%s", hbuf, pbuf);
413     
414     /* client new connection */
415     r = sasl_client_new(service, host, localaddr, remoteaddr, NULL, 0, &conn);
416     if (r != SASL_OK) saslfail(r, "allocating connection state");
417
418     /* set external properties here
419        sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops); */
420
421     /* set required security properties here
422        sasl_setprop(conn, SASL_SEC_PROPS, &secprops); */
423
424     in = fdopen(fd, "r");
425     out = fdopen(fd, "w");
426
427     r = mysasl_negotiate(in, out, conn);
428     if (r == SASL_OK) {
429         /* send/receive data */
430         
431         
432     }
433     
434     printf("closing connection\n");
435     fclose(in);
436     fclose(out);
437     close(fd);
438     sasl_dispose(&conn);
439
440     sasl_done();
441
442     return 0;
443 }