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