1 /* $OpenBSD: aucat.c,v 1.54 2012/04/11 06:05:43 ratchov Exp $ */ 2 /* 3 * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 #include <sys/stat.h> 21 #include <sys/un.h> 22 23 #include <netinet/in.h> 24 #include <netinet/tcp.h> 25 #include <netdb.h> 26 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <limits.h> 30 #include <poll.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include "aucat.h" 37 #include "debug.h" 38 39 40 /* 41 * read a message, return 0 if not completed 42 */ 43 int 44 aucat_rmsg(struct aucat *hdl, int *eof) 45 { 46 ssize_t n; 47 unsigned char *data; 48 49 if (hdl->rstate != RSTATE_MSG) { 50 DPRINTF("aucat_rmsg: bad state\n"); 51 abort(); 52 } 53 while (hdl->rtodo > 0) { 54 data = (unsigned char *)&hdl->rmsg; 55 data += sizeof(struct amsg) - hdl->rtodo; 56 while ((n = read(hdl->fd, data, hdl->rtodo)) < 0) { 57 if (errno == EINTR) 58 continue; 59 if (errno != EAGAIN) { 60 *eof = 1; 61 DPERROR("aucat_rmsg: read"); 62 } 63 return 0; 64 } 65 if (n == 0) { 66 DPRINTF("aucat_rmsg: eof\n"); 67 *eof = 1; 68 return 0; 69 } 70 hdl->rtodo -= n; 71 } 72 if (ntohl(hdl->rmsg.cmd) == AMSG_DATA) { 73 hdl->rtodo = ntohl(hdl->rmsg.u.data.size); 74 hdl->rstate = RSTATE_DATA; 75 } else { 76 hdl->rtodo = sizeof(struct amsg); 77 hdl->rstate = RSTATE_MSG; 78 } 79 return 1; 80 } 81 82 /* 83 * write a message, return 0 if not completed 84 */ 85 int 86 aucat_wmsg(struct aucat *hdl, int *eof) 87 { 88 ssize_t n; 89 unsigned char *data; 90 91 if (hdl->wstate == WSTATE_IDLE) 92 hdl->wstate = WSTATE_MSG; 93 hdl->wtodo = sizeof(struct amsg); 94 if (hdl->wstate != WSTATE_MSG) { 95 DPRINTF("aucat_wmsg: bad state\n"); 96 abort(); 97 } 98 while (hdl->wtodo > 0) { 99 data = (unsigned char *)&hdl->wmsg; 100 data += sizeof(struct amsg) - hdl->wtodo; 101 while ((n = write(hdl->fd, data, hdl->wtodo)) < 0) { 102 if (errno == EINTR) 103 continue; 104 if (errno != EAGAIN) { 105 *eof = 1; 106 DPERROR("aucat_wmsg: write"); 107 } 108 return 0; 109 } 110 hdl->wtodo -= n; 111 } 112 if (ntohl(hdl->wmsg.cmd) == AMSG_DATA) { 113 hdl->wtodo = ntohl(hdl->wmsg.u.data.size); 114 hdl->wstate = WSTATE_DATA; 115 } else { 116 hdl->wtodo = 0xdeadbeef; 117 hdl->wstate = WSTATE_IDLE; 118 } 119 return 1; 120 } 121 122 size_t 123 aucat_rdata(struct aucat *hdl, void *buf, size_t len, int *eof) 124 { 125 ssize_t n; 126 127 if (hdl->rstate != RSTATE_DATA) { 128 DPRINTF("aucat_rdata: bad state\n"); 129 abort(); 130 } 131 if (len > hdl->rtodo) 132 len = hdl->rtodo; 133 while ((n = read(hdl->fd, buf, len)) < 0) { 134 if (errno == EINTR) 135 continue; 136 if (errno != EAGAIN) { 137 *eof = 1; 138 DPERROR("aucat_rdata: read"); 139 } 140 return 0; 141 } 142 if (n == 0) { 143 DPRINTF("aucat_rdata: eof\n"); 144 *eof = 1; 145 return 0; 146 } 147 hdl->rtodo -= n; 148 if (hdl->rtodo == 0) { 149 hdl->rstate = RSTATE_MSG; 150 hdl->rtodo = sizeof(struct amsg); 151 } 152 DPRINTFN(2, "aucat_rdata: read: n = %zd\n", n); 153 return n; 154 } 155 156 size_t 157 aucat_wdata(struct aucat *hdl, const void *buf, size_t len, 158 unsigned int wbpf, int *eof) 159 { 160 ssize_t n; 161 size_t datasize; 162 163 switch (hdl->wstate) { 164 case WSTATE_IDLE: 165 datasize = len; 166 if (datasize > AMSG_DATAMAX) 167 datasize = AMSG_DATAMAX; 168 datasize -= datasize % wbpf; 169 if (datasize == 0) 170 datasize = wbpf; 171 hdl->wmsg.cmd = htonl(AMSG_DATA); 172 hdl->wmsg.u.data.size = htonl(datasize); 173 hdl->wtodo = sizeof(struct amsg); 174 hdl->wstate = WSTATE_MSG; 175 /* FALLTHROUGH */ 176 case WSTATE_MSG: 177 if (!aucat_wmsg(hdl, eof)) 178 return 0; 179 } 180 if (len > hdl->wtodo) 181 len = hdl->wtodo; 182 if (len == 0) { 183 DPRINTF("aucat_wdata: len == 0\n"); 184 abort(); 185 } 186 while ((n = write(hdl->fd, buf, len)) < 0) { 187 if (errno == EINTR) 188 continue; 189 if (errno != EAGAIN) { 190 *eof = 1; 191 DPERROR("aucat_wdata: write"); 192 } 193 return 0; 194 } 195 DPRINTFN(2, "aucat_wdata: write: n = %zd\n", n); 196 hdl->wtodo -= n; 197 if (hdl->wtodo == 0) { 198 hdl->wstate = WSTATE_IDLE; 199 hdl->wtodo = 0xdeadbeef; 200 } 201 return n; 202 } 203 204 int 205 aucat_mkcookie(unsigned char *cookie) 206 { 207 struct stat sb; 208 char buf[PATH_MAX], tmp[PATH_MAX], *path; 209 ssize_t len; 210 int fd; 211 212 /* 213 * try to load the cookie 214 */ 215 path = issetugid() ? NULL : getenv("AUCAT_COOKIE"); 216 if (path == NULL) { 217 path = issetugid() ? NULL : getenv("HOME"); 218 if (path == NULL) 219 goto bad_gen; 220 snprintf(buf, PATH_MAX, "%s/.aucat_cookie", path); 221 path = buf; 222 } 223 fd = open(path, O_RDONLY); 224 if (fd < 0) { 225 if (errno != ENOENT) 226 DPERROR(path); 227 goto bad_gen; 228 } 229 if (fstat(fd, &sb) < 0) { 230 DPERROR(path); 231 goto bad_close; 232 } 233 if (sb.st_mode & 0077) { 234 DPRINTF("%s has wrong permissions\n", path); 235 goto bad_close; 236 } 237 len = read(fd, cookie, AMSG_COOKIELEN); 238 if (len < 0) { 239 DPERROR(path); 240 goto bad_close; 241 } 242 if (len != AMSG_COOKIELEN) { 243 DPRINTF("%s: short read\n", path); 244 goto bad_close; 245 } 246 close(fd); 247 return 1; 248 bad_close: 249 close(fd); 250 bad_gen: 251 /* 252 * generate a new cookie 253 */ 254 arc4random_buf(cookie, AMSG_COOKIELEN); 255 256 /* 257 * try to save the cookie 258 */ 259 if (path == NULL) 260 return 1; 261 if (strlcpy(tmp, path, PATH_MAX) >= PATH_MAX || 262 strlcat(tmp, ".XXXXXXXX", PATH_MAX) >= PATH_MAX) { 263 DPRINTF("%s: too long\n", path); 264 return 1; 265 } 266 fd = mkstemp(tmp); 267 if (fd < 0) { 268 DPERROR(tmp); 269 return 1; 270 } 271 if (write(fd, cookie, AMSG_COOKIELEN) < 0) { 272 DPERROR(tmp); 273 unlink(tmp); 274 close(fd); 275 return 1; 276 } 277 close(fd); 278 if (rename(tmp, path) < 0) { 279 DPERROR(tmp); 280 unlink(tmp); 281 } 282 return 1; 283 } 284 285 int 286 aucat_connect_tcp(struct aucat *hdl, char *host, unsigned int unit) 287 { 288 int s, error, opt; 289 struct addrinfo *ailist, *ai, aihints; 290 char serv[NI_MAXSERV]; 291 292 snprintf(serv, sizeof(serv), "%u", unit + AUCAT_PORT); 293 memset(&aihints, 0, sizeof(struct addrinfo)); 294 aihints.ai_socktype = SOCK_STREAM; 295 aihints.ai_protocol = IPPROTO_TCP; 296 error = getaddrinfo(host, serv, &aihints, &ailist); 297 if (error) { 298 DPRINTF("%s: %s\n", host, gai_strerror(error)); 299 return 0; 300 } 301 s = -1; 302 for (ai = ailist; ai != NULL; ai = ai->ai_next) { 303 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 304 if (s < 0) { 305 DPERROR("socket"); 306 continue; 307 } 308 restart: 309 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) { 310 if (errno == EINTR) 311 goto restart; 312 DPERROR("connect"); 313 close(s); 314 s = -1; 315 continue; 316 } 317 break; 318 } 319 freeaddrinfo(ailist); 320 if (s < 0) 321 return 0; 322 opt = 1; 323 if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)) < 0) { 324 DPERROR("setsockopt"); 325 close(s); 326 return 0; 327 } 328 hdl->fd = s; 329 return 1; 330 } 331 332 int 333 aucat_connect_un(struct aucat *hdl, unsigned int unit) 334 { 335 struct sockaddr_un ca; 336 socklen_t len = sizeof(struct sockaddr_un); 337 uid_t uid; 338 int s; 339 340 uid = geteuid(); 341 snprintf(ca.sun_path, sizeof(ca.sun_path), 342 "/tmp/aucat-%u/%s%u", uid, AUCAT_PATH, unit); 343 ca.sun_family = AF_UNIX; 344 s = socket(AF_UNIX, SOCK_STREAM, 0); 345 if (s < 0) 346 return 0; 347 while (connect(s, (struct sockaddr *)&ca, len) < 0) { 348 if (errno == EINTR) 349 continue; 350 DPERROR(ca.sun_path); 351 /* try shared server */ 352 snprintf(ca.sun_path, sizeof(ca.sun_path), 353 "/tmp/aucat/%s%u", AUCAT_PATH, unit); 354 while (connect(s, (struct sockaddr *)&ca, len) < 0) { 355 if (errno == EINTR) 356 continue; 357 DPERROR(ca.sun_path); 358 close(s); 359 return 0; 360 } 361 break; 362 } 363 hdl->fd = s; 364 return 1; 365 } 366 367 static const char * 368 parsedev(const char *str, unsigned int *rval) 369 { 370 const char *p = str; 371 unsigned int val; 372 373 for (val = 0; *p >= '0' && *p <= '9'; p++) { 374 val = 10 * val + (*p - '0'); 375 if (val >= 16) { 376 DPRINTF("%s: number too large\n", str); 377 return NULL; 378 } 379 } 380 if (p == str) { 381 DPRINTF("%s: number expected\n", str); 382 return NULL; 383 } 384 *rval = val; 385 return p; 386 } 387 388 static const char * 389 parsestr(const char *str, char *rstr, unsigned int max) 390 { 391 const char *p = str; 392 393 while (*p != '\0' && *p != ',' && *p != '/') { 394 if (--max == 0) { 395 DPRINTF("%s: string too long\n", str); 396 return NULL; 397 } 398 *rstr++ = *p++; 399 } 400 if (str == p) { 401 DPRINTF("%s: string expected\n", str); 402 return NULL; 403 } 404 *rstr = '\0'; 405 return p; 406 } 407 408 int 409 aucat_open(struct aucat *hdl, const char *str, unsigned int mode, 410 unsigned int type) 411 { 412 extern char *__progname; 413 int eof; 414 char host[NI_MAXHOST], opt[AMSG_OPTMAX]; 415 const char *p = str; 416 unsigned int unit, devnum; 417 418 if (*p == '@') { 419 p = parsestr(++p, host, NI_MAXHOST); 420 if (p == NULL) 421 return 0; 422 } else 423 *host = '\0'; 424 if (*p == ',') { 425 p = parsedev(++p, &unit); 426 if (p == NULL) 427 return 0; 428 } else 429 unit = 0; 430 if (*p != '/' && *p != ':') { 431 DPRINTF("%s: '/' expected\n", str); 432 return 0; 433 } 434 p = parsedev(++p, &devnum); 435 if (p == NULL) 436 return 0; 437 if (*p == '.') { 438 p = parsestr(++p, opt, AMSG_OPTMAX); 439 if (p == NULL) 440 return 0; 441 } else 442 strlcpy(opt, "default", AMSG_OPTMAX); 443 if (*p != '\0') { 444 DPRINTF("%s: junk at end of dev name\n", p); 445 return 0; 446 } 447 if (type) 448 devnum += 16; /* XXX */ 449 DPRINTF("aucat_open: host=%s unit=%u devnum=%u opt=%s\n", 450 host, unit, devnum, opt); 451 if (host[0] != '\0') { 452 if (!aucat_connect_tcp(hdl, host, unit)) 453 return 0; 454 } else { 455 if (!aucat_connect_un(hdl, unit)) 456 return 0; 457 } 458 if (fcntl(hdl->fd, F_SETFD, FD_CLOEXEC) < 0) { 459 DPERROR("FD_CLOEXEC"); 460 goto bad_connect; 461 } 462 hdl->rstate = RSTATE_MSG; 463 hdl->rtodo = sizeof(struct amsg); 464 hdl->wstate = WSTATE_IDLE; 465 hdl->wtodo = 0xdeadbeef; 466 467 /* 468 * say hello to server 469 */ 470 AMSG_INIT(&hdl->wmsg); 471 hdl->wmsg.cmd = htonl(AMSG_AUTH); 472 if (!aucat_mkcookie(hdl->wmsg.u.auth.cookie)) 473 goto bad_connect; 474 hdl->wtodo = sizeof(struct amsg); 475 if (!aucat_wmsg(hdl, &eof)) 476 goto bad_connect; 477 AMSG_INIT(&hdl->wmsg); 478 hdl->wmsg.cmd = htonl(AMSG_HELLO); 479 hdl->wmsg.u.hello.version = AMSG_VERSION; 480 hdl->wmsg.u.hello.mode = htons(mode); 481 hdl->wmsg.u.hello.devnum = devnum; 482 strlcpy(hdl->wmsg.u.hello.who, __progname, 483 sizeof(hdl->wmsg.u.hello.who)); 484 strlcpy(hdl->wmsg.u.hello.opt, opt, 485 sizeof(hdl->wmsg.u.hello.opt)); 486 hdl->wtodo = sizeof(struct amsg); 487 if (!aucat_wmsg(hdl, &eof)) 488 goto bad_connect; 489 hdl->rtodo = sizeof(struct amsg); 490 if (!aucat_rmsg(hdl, &eof)) { 491 DPRINTF("aucat_init: mode refused\n"); 492 goto bad_connect; 493 } 494 if (ntohl(hdl->rmsg.cmd) != AMSG_ACK) { 495 DPRINTF("aucat_init: protocol err\n"); 496 goto bad_connect; 497 } 498 return 1; 499 bad_connect: 500 while (close(hdl->fd) < 0 && errno == EINTR) 501 ; /* retry */ 502 return 0; 503 } 504 505 void 506 aucat_close(struct aucat *hdl, int eof) 507 { 508 char dummy[1]; 509 510 if (!eof) { 511 AMSG_INIT(&hdl->wmsg); 512 hdl->wmsg.cmd = htonl(AMSG_BYE); 513 hdl->wtodo = sizeof(struct amsg); 514 if (!aucat_wmsg(hdl, &eof)) 515 goto bad_close; 516 while (read(hdl->fd, dummy, 1) < 0 && errno == EINTR) 517 ; /* nothing */ 518 } 519 bad_close: 520 while (close(hdl->fd) < 0 && errno == EINTR) 521 ; /* nothing */ 522 } 523 524 int 525 aucat_setfl(struct aucat *hdl, int nbio, int *eof) 526 { 527 if (fcntl(hdl->fd, F_SETFL, nbio ? O_NONBLOCK : 0) < 0) { 528 DPERROR("aucat_setfl: fcntl"); 529 *eof = 1; 530 return 0; 531 } 532 return 1; 533 } 534 535 int 536 aucat_pollfd(struct aucat *hdl, struct pollfd *pfd, int events) 537 { 538 if (hdl->rstate == RSTATE_MSG) 539 events |= POLLIN; 540 pfd->fd = hdl->fd; 541 pfd->events = events; 542 return 1; 543 } 544 545 int 546 aucat_revents(struct aucat *hdl, struct pollfd *pfd) 547 { 548 int revents = pfd->revents; 549 550 DPRINTFN(2, "aucat_revents: revents: %x\n", revents); 551 return revents; 552 } 553