1/* $Id: client.c,v 1.2 2004/07/07 22:53:07 snsimon 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 */
67static 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
81static 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
106static 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
137static char *
138getpassphrase(const char *prompt)
139{
140  return getpass(prompt);
141}
142#endif /* ! HAVE_GETPASSPHRASE */
143
144static int
145getsecret(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 */
181static 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
195int 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
228char *mech;
229
230int 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
318void usage(void)
319{
320    fprintf(stderr, "usage: client [-p port] [-s service] [-m mech] host\n");
321    exit(EX_USAGE);
322}
323
324int 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}
444