1 | /* $Id: client.c,v 1.1.1.1 2002-10-13 18:01:31 ghudson Exp $ */ |
---|
2 | /* |
---|
3 | * Copyright (c) 2001 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 | struct sockaddr_storage local_ip, remote_ip; |
---|
339 | |
---|
340 | while ((c = getopt(argc, argv, "p:s:m:")) != EOF) { |
---|
341 | switch(c) { |
---|
342 | case 'p': |
---|
343 | port = optarg; |
---|
344 | break; |
---|
345 | |
---|
346 | case 's': |
---|
347 | service = optarg; |
---|
348 | break; |
---|
349 | |
---|
350 | case 'm': |
---|
351 | mech = optarg; |
---|
352 | break; |
---|
353 | |
---|
354 | default: |
---|
355 | usage(); |
---|
356 | break; |
---|
357 | } |
---|
358 | } |
---|
359 | |
---|
360 | if (optind > argc - 1) { |
---|
361 | usage(); |
---|
362 | } |
---|
363 | if (optind == argc - 1) { |
---|
364 | host = argv[optind]; |
---|
365 | } |
---|
366 | |
---|
367 | /* initialize the sasl library */ |
---|
368 | r = sasl_client_init(callbacks); |
---|
369 | if (r != SASL_OK) saslfail(r, "initializing libsasl"); |
---|
370 | |
---|
371 | /* connect to remote server */ |
---|
372 | fd = getconn(host, port); |
---|
373 | |
---|
374 | /* set ip addresses */ |
---|
375 | salen = sizeof(local_ip); |
---|
376 | if (getsockname(fd, (struct sockaddr *)&local_ip, &salen) < 0) { |
---|
377 | perror("getsockname"); |
---|
378 | } |
---|
379 | |
---|
380 | getnameinfo((struct sockaddr *)&local_ip, salen, |
---|
381 | hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), |
---|
382 | NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV); |
---|
383 | snprintf(localaddr, sizeof(localaddr), "%s;%s", hbuf, pbuf); |
---|
384 | |
---|
385 | salen = sizeof(remote_ip); |
---|
386 | if (getpeername(fd, (struct sockaddr *)&remote_ip, &salen) < 0) { |
---|
387 | perror("getpeername"); |
---|
388 | } |
---|
389 | |
---|
390 | getnameinfo((struct sockaddr *)&remote_ip, salen, |
---|
391 | hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), |
---|
392 | NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV); |
---|
393 | snprintf(remoteaddr, sizeof(remoteaddr), "%s;%s", hbuf, pbuf); |
---|
394 | |
---|
395 | /* client new connection */ |
---|
396 | r = sasl_client_new(service, host, localaddr, remoteaddr, NULL, 0, &conn); |
---|
397 | if (r != SASL_OK) saslfail(r, "allocating connection state"); |
---|
398 | |
---|
399 | /* set external properties here |
---|
400 | sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops); */ |
---|
401 | |
---|
402 | /* set required security properties here |
---|
403 | sasl_setprop(conn, SASL_SEC_PROPS, &secprops); */ |
---|
404 | |
---|
405 | in = fdopen(fd, "r"); |
---|
406 | out = fdopen(fd, "w"); |
---|
407 | |
---|
408 | r = mysasl_negotiate(in, out, conn); |
---|
409 | if (r == SASL_OK) { |
---|
410 | /* send/receive data */ |
---|
411 | |
---|
412 | |
---|
413 | } |
---|
414 | |
---|
415 | printf("closing connection\n"); |
---|
416 | fclose(in); |
---|
417 | fclose(out); |
---|
418 | close(fd); |
---|
419 | sasl_dispose(&conn); |
---|
420 | |
---|
421 | sasl_done(); |
---|
422 | |
---|
423 | return 0; |
---|
424 | } |
---|