1 /* $OpenBSD: aucat.c,v 1.61 2013/12/20 08:51:28 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 static 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 static 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 static 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 DPRINTFN(2, "%s: connected\n", ca.sun_path); 365 return 1; 366 } 367 368 static const char * 369 parsedev(const char *str, unsigned int *rval) 370 { 371 const char *p = str; 372 unsigned int val; 373 374 for (val = 0; *p >= '0' && *p <= '9'; p++) { 375 val = 10 * val + (*p - '0'); 376 if (val >= 16) { 377 DPRINTF("%s: number too large\n", str); 378 return NULL; 379 } 380 } 381 if (p == str) { 382 DPRINTF("%s: number expected\n", str); 383 return NULL; 384 } 385 *rval = val; 386 return p; 387 } 388 389 static const char * 390 parsestr(const char *str, char *rstr, unsigned int max) 391 { 392 const char *p = str; 393 394 while (*p != '\0' && *p != ',' && *p != '/') { 395 if (--max == 0) { 396 DPRINTF("%s: string too long\n", str); 397 return NULL; 398 } 399 *rstr++ = *p++; 400 } 401 if (str == p) { 402 DPRINTF("%s: string expected\n", str); 403 return NULL; 404 } 405 *rstr = '\0'; 406 return p; 407 } 408 409 int 410 _aucat_open(struct aucat *hdl, const char *str, unsigned int mode, 411 unsigned int type) 412 { 413 extern char *__progname; 414 int eof; 415 char host[NI_MAXHOST], opt[AMSG_OPTMAX]; 416 const char *p = str; 417 unsigned int unit, devnum; 418 419 if (*p == '@') { 420 p = parsestr(++p, host, NI_MAXHOST); 421 if (p == NULL) 422 return 0; 423 } else 424 *host = '\0'; 425 if (*p == ',') { 426 p = parsedev(++p, &unit); 427 if (p == NULL) 428 return 0; 429 } else 430 unit = 0; 431 if (*p != '/' && *p != ':') { 432 DPRINTF("%s: '/' expected\n", str); 433 return 0; 434 } 435 p = parsedev(++p, &devnum); 436 if (p == NULL) 437 return 0; 438 if (*p == '.') { 439 p = parsestr(++p, opt, AMSG_OPTMAX); 440 if (p == NULL) 441 return 0; 442 } else 443 strlcpy(opt, "default", AMSG_OPTMAX); 444 if (*p != '\0') { 445 DPRINTF("%s: junk at end of dev name\n", p); 446 return 0; 447 } 448 devnum += type * 16; /* XXX */ 449 DPRINTFN(2, "_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 hdl->maxwrite = 0; 467 468 /* 469 * say hello to server 470 */ 471 AMSG_INIT(&hdl->wmsg); 472 hdl->wmsg.cmd = htonl(AMSG_AUTH); 473 if (!aucat_mkcookie(hdl->wmsg.u.auth.cookie)) 474 goto bad_connect; 475 hdl->wtodo = sizeof(struct amsg); 476 if (!_aucat_wmsg(hdl, &eof)) 477 goto bad_connect; 478 AMSG_INIT(&hdl->wmsg); 479 hdl->wmsg.cmd = htonl(AMSG_HELLO); 480 hdl->wmsg.u.hello.version = AMSG_VERSION; 481 hdl->wmsg.u.hello.mode = htons(mode); 482 hdl->wmsg.u.hello.devnum = devnum; 483 strlcpy(hdl->wmsg.u.hello.who, __progname, 484 sizeof(hdl->wmsg.u.hello.who)); 485 strlcpy(hdl->wmsg.u.hello.opt, opt, 486 sizeof(hdl->wmsg.u.hello.opt)); 487 hdl->wtodo = sizeof(struct amsg); 488 if (!_aucat_wmsg(hdl, &eof)) 489 goto bad_connect; 490 hdl->rtodo = sizeof(struct amsg); 491 if (!_aucat_rmsg(hdl, &eof)) { 492 DPRINTF("aucat_init: mode refused\n"); 493 goto bad_connect; 494 } 495 if (ntohl(hdl->rmsg.cmd) != AMSG_ACK) { 496 DPRINTF("aucat_init: protocol err\n"); 497 goto bad_connect; 498 } 499 return 1; 500 bad_connect: 501 while (close(hdl->fd) < 0 && errno == EINTR) 502 ; /* retry */ 503 return 0; 504 } 505 506 void 507 _aucat_close(struct aucat *hdl, int eof) 508 { 509 char dummy[1]; 510 511 if (!eof) { 512 AMSG_INIT(&hdl->wmsg); 513 hdl->wmsg.cmd = htonl(AMSG_BYE); 514 hdl->wtodo = sizeof(struct amsg); 515 if (!_aucat_wmsg(hdl, &eof)) 516 goto bad_close; 517 while (read(hdl->fd, dummy, 1) < 0 && errno == EINTR) 518 ; /* nothing */ 519 } 520 bad_close: 521 while (close(hdl->fd) < 0 && errno == EINTR) 522 ; /* nothing */ 523 } 524 525 int 526 _aucat_setfl(struct aucat *hdl, int nbio, int *eof) 527 { 528 if (fcntl(hdl->fd, F_SETFL, nbio ? O_NONBLOCK : 0) < 0) { 529 DPERROR("_aucat_setfl: fcntl"); 530 *eof = 1; 531 return 0; 532 } 533 return 1; 534 } 535 536 int 537 _aucat_pollfd(struct aucat *hdl, struct pollfd *pfd, int events) 538 { 539 if (hdl->rstate == RSTATE_MSG) 540 events |= POLLIN; 541 pfd->fd = hdl->fd; 542 pfd->events = events; 543 return 1; 544 } 545 546 int 547 _aucat_revents(struct aucat *hdl, struct pollfd *pfd) 548 { 549 int revents = pfd->revents; 550 551 DPRINTFN(2, "_aucat_revents: revents: %x\n", revents); 552 return revents; 553 } 554