1 /* $Id: receiver.c,v 1.22 2019/03/30 07:28:55 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 5 * Copyright (c) 2019 Florian Obser <florian@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 #include <sys/mman.h> 20 #include <sys/stat.h> 21 22 #include <assert.h> 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <inttypes.h> 26 #include <math.h> 27 #include <poll.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <time.h> 32 #include <unistd.h> 33 34 #include "extern.h" 35 36 enum pfdt { 37 PFD_SENDER_IN = 0, /* input from the sender */ 38 PFD_UPLOADER_IN, /* uploader input from a local file */ 39 PFD_DOWNLOADER_IN, /* downloader input from a local file */ 40 PFD_SENDER_OUT, /* output to the sender */ 41 PFD__MAX 42 }; 43 44 int 45 rsync_set_metadata(struct sess *sess, int newfile, 46 int fd, const struct flist *f, const char *path) 47 { 48 uid_t uid = (uid_t)-1; 49 gid_t gid = (gid_t)-1; 50 mode_t mode; 51 struct timespec ts[2]; 52 53 /* Conditionally adjust file modification time. */ 54 55 if (sess->opts->preserve_times) { 56 ts[0].tv_nsec = UTIME_NOW; 57 ts[1].tv_sec = f->st.mtime; 58 ts[1].tv_nsec = 0; 59 if (futimens(fd, ts) == -1) { 60 ERR(sess, "%s: futimens", path); 61 return 0; 62 } 63 LOG4(sess, "%s: updated date", f->path); 64 } 65 66 /* 67 * Conditionally adjust identifiers. 68 * If we have an EPERM, report it but continue on: this just 69 * means that we're mapping into an unknown (or disallowed) 70 * group identifier. 71 */ 72 if (getuid() == 0 && sess->opts->preserve_uids) 73 uid = f->st.uid; 74 if (sess->opts->preserve_gids) 75 gid = f->st.gid; 76 77 mode = f->st.mode; 78 if (uid != (uid_t)-1 || gid != (gid_t)-1) { 79 if (fchown(fd, uid, gid) == -1) { 80 if (errno != EPERM) { 81 ERR(sess, "%s: fchown", path); 82 return 0; 83 } 84 if (getuid() == 0) 85 WARNX(sess, "%s: identity unknown or not available " 86 "to user.group: %u.%u", f->path, uid, gid); 87 } else 88 LOG4(sess, "%s: updated uid and/or gid", f->path); 89 mode &= ~(S_ISTXT | S_ISUID | S_ISGID); 90 } 91 92 /* Conditionally adjust file permissions. */ 93 94 if (newfile || sess->opts->preserve_perms) { 95 if (fchmod(fd, mode) == -1) { 96 ERR(sess, "%s: fchmod", path); 97 return 0; 98 } 99 LOG4(sess, "%s: updated permissions", f->path); 100 } 101 102 return 1; 103 } 104 105 int 106 rsync_set_metadata_at(struct sess *sess, int newfile, int rootfd, 107 const struct flist *f, const char *path) 108 { 109 uid_t uid = (uid_t)-1; 110 gid_t gid = (gid_t)-1; 111 mode_t mode; 112 struct timespec ts[2]; 113 114 /* Conditionally adjust file modification time. */ 115 116 if (sess->opts->preserve_times) { 117 ts[0].tv_nsec = UTIME_NOW; 118 ts[1].tv_sec = f->st.mtime; 119 ts[1].tv_nsec = 0; 120 if (utimensat(rootfd, path, ts, AT_SYMLINK_NOFOLLOW) == -1) { 121 ERR(sess, "%s: utimensat", path); 122 return 0; 123 } 124 LOG4(sess, "%s: updated date", f->path); 125 } 126 127 /* 128 * Conditionally adjust identifiers. 129 * If we have an EPERM, report it but continue on: this just 130 * means that we're mapping into an unknown (or disallowed) 131 * group identifier. 132 */ 133 if (getuid() == 0 && sess->opts->preserve_uids) 134 uid = f->st.uid; 135 if (sess->opts->preserve_gids) 136 gid = f->st.gid; 137 138 mode = f->st.mode; 139 if (uid != (uid_t)-1 || gid != (gid_t)-1) { 140 if (fchownat(rootfd, path, uid, gid, AT_SYMLINK_NOFOLLOW) == -1) { 141 if (errno != EPERM) { 142 ERR(sess, "%s: fchownat", path); 143 return 0; 144 } 145 if (getuid() == 0) 146 WARNX(sess, "%s: identity unknown or not available " 147 "to user.group: %u.%u", f->path, uid, gid); 148 } else 149 LOG4(sess, "%s: updated uid and/or gid", f->path); 150 mode &= ~(S_ISTXT | S_ISUID | S_ISGID); 151 } 152 153 /* Conditionally adjust file permissions. */ 154 155 if (newfile || sess->opts->preserve_perms) { 156 if (fchmodat(rootfd, path, mode, AT_SYMLINK_NOFOLLOW) == -1) { 157 ERR(sess, "%s: fchmodat", path); 158 return 0; 159 } 160 LOG4(sess, "%s: updated permissions", f->path); 161 } 162 163 return 1; 164 } 165 166 /* 167 * Pledges: unveil, unix, rpath, cpath, wpath, stdio, fattr, chown. 168 * Pledges (dry-run): -unix, -cpath, -wpath, -fattr, -chown. 169 */ 170 int 171 rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root) 172 { 173 struct flist *fl = NULL, *dfl = NULL; 174 size_t i, flsz = 0, dflsz = 0, excl; 175 char *tofree; 176 int rc = 0, dfd = -1, phase = 0, c; 177 int32_t ioerror; 178 struct pollfd pfd[PFD__MAX]; 179 struct download *dl = NULL; 180 struct upload *ul = NULL; 181 mode_t oumask; 182 183 if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) { 184 ERR(sess, "pledge"); 185 goto out; 186 } 187 188 /* Client sends zero-length exclusions. */ 189 190 if (!sess->opts->server && 191 !io_write_int(sess, fdout, 0)) { 192 ERRX1(sess, "io_write_int"); 193 goto out; 194 } 195 196 if (sess->opts->server && sess->opts->del) { 197 if (!io_read_size(sess, fdin, &excl)) { 198 ERRX1(sess, "io_read_size"); 199 goto out; 200 } else if (excl != 0) { 201 ERRX(sess, "exclusion list is non-empty"); 202 goto out; 203 } 204 } 205 206 /* 207 * Start by receiving the file list and our mystery number. 208 * These we're going to be touching on our local system. 209 */ 210 211 if (!flist_recv(sess, fdin, &fl, &flsz)) { 212 ERRX1(sess, "flist_recv"); 213 goto out; 214 } 215 216 /* The IO error is sent after the file list. */ 217 218 if (!io_read_int(sess, fdin, &ioerror)) { 219 ERRX1(sess, "io_read_int"); 220 goto out; 221 } else if (ioerror != 0) { 222 ERRX1(sess, "io_error is non-zero"); 223 goto out; 224 } 225 226 if (flsz == 0 && !sess->opts->server) { 227 WARNX(sess, "receiver has empty file list: exiting"); 228 rc = 1; 229 goto out; 230 } else if (!sess->opts->server) 231 LOG1(sess, "Transfer starting: %zu files", flsz); 232 233 LOG2(sess, "%s: receiver destination", root); 234 235 /* 236 * Create the path for our destination directory, if we're not 237 * in dry-run mode (which would otherwise crash w/the pledge). 238 * This uses our current umask: we might set the permissions on 239 * this directory in post_dir(). 240 */ 241 242 if (!sess->opts->dry_run) { 243 if ((tofree = strdup(root)) == NULL) { 244 ERR(sess, "strdup"); 245 goto out; 246 } else if (mkpath(sess, tofree) < 0) { 247 ERRX1(sess, "%s: mkpath", root); 248 free(tofree); 249 goto out; 250 } 251 free(tofree); 252 } 253 254 /* 255 * Disable umask() so we can set permissions fully. 256 * Then open the directory iff we're not in dry_run. 257 */ 258 259 oumask = umask(0); 260 261 if (!sess->opts->dry_run) { 262 dfd = open(root, O_RDONLY | O_DIRECTORY, 0); 263 if (dfd == -1) { 264 ERR(sess, "%s: open", root); 265 goto out; 266 } 267 } 268 269 /* 270 * Begin by conditionally getting all files we have currently 271 * available in our destination. 272 */ 273 274 if (sess->opts->del && 275 sess->opts->recursive && 276 !flist_gen_dels(sess, root, &dfl, &dflsz, fl, flsz)) { 277 ERRX1(sess, "flist_gen_local"); 278 goto out; 279 } 280 281 /* 282 * Make our entire view of the file-system be limited to what's 283 * in the root directory. 284 * This prevents us from accidentally (or "under the influence") 285 * writing into other parts of the file-system. 286 */ 287 288 if (unveil(root, "rwc") == -1) { 289 ERR(sess, "%s: unveil", root); 290 goto out; 291 } else if (unveil(NULL, NULL) == -1) { 292 ERR(sess, "%s: unveil", root); 293 goto out; 294 } 295 296 /* If we have a local set, go for the deletion. */ 297 298 if (!flist_del(sess, dfd, dfl, dflsz)) { 299 ERRX1(sess, "flist_del"); 300 goto out; 301 } 302 303 /* Initialise poll events to listen from the sender. */ 304 305 pfd[PFD_SENDER_IN].fd = fdin; 306 pfd[PFD_UPLOADER_IN].fd = -1; 307 pfd[PFD_DOWNLOADER_IN].fd = -1; 308 pfd[PFD_SENDER_OUT].fd = fdout; 309 310 pfd[PFD_SENDER_IN].events = POLLIN; 311 pfd[PFD_UPLOADER_IN].events = POLLIN; 312 pfd[PFD_DOWNLOADER_IN].events = POLLIN; 313 pfd[PFD_SENDER_OUT].events = POLLOUT; 314 315 ul = upload_alloc(sess, root, dfd, fdout, 316 CSUM_LENGTH_PHASE1, fl, flsz, oumask); 317 if (ul == NULL) { 318 ERRX1(sess, "upload_alloc"); 319 goto out; 320 } 321 322 dl = download_alloc(sess, fdin, fl, flsz, dfd); 323 if (dl == NULL) { 324 ERRX1(sess, "download_alloc"); 325 goto out; 326 } 327 328 LOG2(sess, "%s: ready for phase 1 data", root); 329 330 for (;;) { 331 if ((c = poll(pfd, PFD__MAX, POLL_TIMEOUT)) == -1) { 332 ERR(sess, "poll"); 333 goto out; 334 } else if (c == 0) { 335 ERRX(sess, "poll: timeout"); 336 goto out; 337 } 338 339 for (i = 0; i < PFD__MAX; i++) 340 if (pfd[i].revents & (POLLERR|POLLNVAL)) { 341 ERRX(sess, "poll: bad fd"); 342 goto out; 343 } else if (pfd[i].revents & POLLHUP) { 344 ERRX(sess, "poll: hangup"); 345 goto out; 346 } 347 348 /* 349 * If we have a read event and we're multiplexing, we 350 * might just have error messages in the pipe. 351 * It's important to flush these out so that we don't 352 * clog the pipe. 353 * Unset our polling status if there's nothing that 354 * remains in the pipe. 355 */ 356 357 if (sess->mplex_reads && 358 (POLLIN & pfd[PFD_SENDER_IN].revents)) { 359 if (!io_read_flush(sess, fdin)) { 360 ERRX1(sess, "io_read_flush"); 361 goto out; 362 } else if (sess->mplex_read_remain == 0) 363 pfd[PFD_SENDER_IN].revents &= ~POLLIN; 364 } 365 366 367 /* 368 * We run the uploader if we have files left to examine 369 * (i < flsz) or if we have a file that we've opened and 370 * is read to mmap. 371 */ 372 373 if ((POLLIN & pfd[PFD_UPLOADER_IN].revents) || 374 (POLLOUT & pfd[PFD_SENDER_OUT].revents)) { 375 c = rsync_uploader(ul, 376 &pfd[PFD_UPLOADER_IN].fd, 377 sess, &pfd[PFD_SENDER_OUT].fd); 378 if (c < 0) { 379 ERRX1(sess, "rsync_uploader"); 380 goto out; 381 } 382 } 383 384 /* 385 * We need to run the downloader when we either have 386 * read events from the sender or an asynchronous local 387 * open is ready. 388 * XXX: we don't disable PFD_SENDER_IN like with the 389 * uploader because we might stop getting error 390 * messages, which will otherwise clog up the pipes. 391 */ 392 393 if ((POLLIN & pfd[PFD_SENDER_IN].revents) || 394 (POLLIN & pfd[PFD_DOWNLOADER_IN].revents)) { 395 c = rsync_downloader(dl, sess, 396 &pfd[PFD_DOWNLOADER_IN].fd); 397 if (c < 0) { 398 ERRX1(sess, "rsync_downloader"); 399 goto out; 400 } else if (c == 0) { 401 assert(phase == 0); 402 phase++; 403 LOG2(sess, 404 "%s: receiver ready for phase 2 data", 405 root); 406 break; 407 } 408 409 /* 410 * FIXME: if we have any errors during the 411 * download, most notably files getting out of 412 * sync between the send and the receiver, then 413 * here we should bump our checksum length and 414 * go into the second phase. 415 */ 416 } 417 } 418 419 /* Properly close us out by progressing through the phases. */ 420 421 if (phase == 1) { 422 if (!io_write_int(sess, fdout, -1)) { 423 ERRX1(sess, "io_write_int"); 424 goto out; 425 } else if (!io_read_int(sess, fdin, &ioerror)) { 426 ERRX1(sess, "io_read_int"); 427 goto out; 428 } else if (ioerror != -1) { 429 ERRX(sess, "expected phase ack"); 430 goto out; 431 } 432 } 433 434 /* 435 * Now all of our transfers are complete, so we can fix up our 436 * directory permissions. 437 */ 438 439 if (!rsync_uploader_tail(ul, sess)) { 440 ERRX1(sess, "rsync_uploader_tail"); 441 goto out; 442 } 443 444 /* Process server statistics and say good-bye. */ 445 446 if (!sess_stats_recv(sess, fdin)) { 447 ERRX1(sess, "sess_stats_recv"); 448 goto out; 449 } else if (!io_write_int(sess, fdout, -1)) { 450 ERRX1(sess, "io_write_int"); 451 goto out; 452 } 453 454 LOG2(sess, "receiver finished updating"); 455 rc = 1; 456 out: 457 if (dfd != -1) 458 close(dfd); 459 upload_free(ul); 460 download_free(dl); 461 flist_free(fl, flsz); 462 flist_free(dfl, dflsz); 463 return rc; 464 } 465