1 /* $NetBSD: psbuf.c,v 1.20 2025/12/22 16:30:49 nia Exp $ */ 2 3 /* 4 * Copyright (c) 2006-2009 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 #ifndef lint 30 __RCSID("$NetBSD: psbuf.c,v 1.20 2025/12/22 16:30:49 nia Exp $"); 31 #endif /* !lint */ 32 33 /* 34 * buffering functions for network input/output. slightly different 35 * from the average joe buffer routines, as is usually the case ... 36 * these use efuns for now. 37 */ 38 39 #include <sys/types.h> 40 #include <sys/time.h> 41 #include <sys/vnode.h> 42 #include <sys/socket.h> 43 44 #include <err.h> 45 #include <errno.h> 46 #include <endian.h> 47 #include <stdlib.h> 48 #include <util.h> 49 #include <unistd.h> 50 51 #include "psshfs.h" 52 #include "sftp_proto.h" 53 54 #define FAILRV(x) do { int rv; if ((rv=x)) return (rv); } while (/*CONSTCOND*/0) 55 #define READSTATE_LENGTH(off) (off < 4) 56 57 #define SFTP_LENOFF 0 58 #define SFTP_TYPEOFF 4 59 #define SFTP_REQIDOFF 5 60 61 #define CHECK(v) if (!(v)) abort() 62 63 uint8_t 64 psbuf_get_type(struct puffs_framebuf *pb) 65 { 66 uint8_t type; 67 68 puffs_framebuf_getdata_atoff(pb, SFTP_TYPEOFF, &type, 1); 69 return type; 70 } 71 72 uint32_t 73 psbuf_get_len(struct puffs_framebuf *pb) 74 { 75 uint32_t len; 76 77 puffs_framebuf_getdata_atoff(pb, SFTP_LENOFF, &len, 4); 78 return be32toh(len); 79 } 80 81 uint32_t 82 psbuf_get_reqid(struct puffs_framebuf *pb) 83 { 84 uint32_t req; 85 86 puffs_framebuf_getdata_atoff(pb, SFTP_REQIDOFF, &req, 4); 87 return be32toh(req); 88 } 89 90 #define CUROFF(pb) (puffs_framebuf_telloff(pb)) 91 int 92 psbuf_read(struct puffs_usermount *pu, struct puffs_framebuf *pb, 93 int fd, int *done) 94 { 95 void *win; 96 ssize_t n; 97 size_t howmuch, winlen; 98 int lenstate; 99 100 the_next_level: 101 if ((lenstate = READSTATE_LENGTH(CUROFF(pb)))) 102 howmuch = 4 - CUROFF(pb); 103 else 104 howmuch = psbuf_get_len(pb) - (CUROFF(pb) - 4); 105 106 if (puffs_framebuf_reserve_space(pb, howmuch) == -1) 107 return errno; 108 109 while (howmuch) { 110 winlen = howmuch; 111 if (puffs_framebuf_getwindow(pb, CUROFF(pb), &win, &winlen)==-1) 112 return errno; 113 n = recv(fd, win, winlen, MSG_NOSIGNAL); 114 switch (n) { 115 case 0: 116 return ECONNRESET; 117 case -1: 118 if (errno == EAGAIN) 119 return 0; 120 return errno; 121 default: 122 howmuch -= n; 123 puffs_framebuf_seekset(pb, CUROFF(pb) + n); 124 break; 125 } 126 } 127 128 if (!lenstate) { 129 /* XXX: initial exchange shorter.. but don't worry, be happy */ 130 puffs_framebuf_seekset(pb, 9); 131 *done = 1; 132 return 0; 133 } else 134 goto the_next_level; 135 } 136 137 int 138 psbuf_write(struct puffs_usermount *pu, struct puffs_framebuf *pb, 139 int fd, int *done) 140 { 141 void *win; 142 ssize_t n; 143 size_t winlen, howmuch; 144 145 /* finalize buffer.. could be elsewhere ... */ 146 if (CUROFF(pb) == 0) { 147 uint32_t len; 148 149 len = htobe32(puffs_framebuf_tellsize(pb) - 4); 150 puffs_framebuf_putdata_atoff(pb, 0, &len, 4); 151 } 152 153 howmuch = puffs_framebuf_tellsize(pb) - CUROFF(pb); 154 while (howmuch) { 155 winlen = howmuch; 156 if (puffs_framebuf_getwindow(pb, CUROFF(pb), &win, &winlen)==-1) 157 return errno; 158 n = send(fd, win, winlen, MSG_NOSIGNAL); 159 switch (n) { 160 case 0: 161 return ECONNRESET; 162 case -1: 163 if (errno == EAGAIN) 164 return 0; 165 return errno; 166 default: 167 howmuch -= n; 168 puffs_framebuf_seekset(pb, CUROFF(pb) + n); 169 break; 170 } 171 } 172 173 *done = 1; 174 return 0; 175 } 176 #undef CUROFF 177 178 int 179 psbuf_cmp(struct puffs_usermount *pu, 180 struct puffs_framebuf *cmp1, struct puffs_framebuf *cmp2, int *notresp) 181 { 182 183 return psbuf_get_reqid(cmp1) != psbuf_get_reqid(cmp2); 184 } 185 186 struct puffs_framebuf * 187 psbuf_makeout() 188 { 189 struct puffs_framebuf *pb; 190 191 pb = puffs_framebuf_make(); 192 puffs_framebuf_seekset(pb, 4); 193 return pb; 194 } 195 196 void 197 psbuf_recycleout(struct puffs_framebuf *pb) 198 { 199 200 puffs_framebuf_recycle(pb); 201 puffs_framebuf_seekset(pb, 4); 202 } 203 204 void 205 psbuf_put_1(struct puffs_framebuf *pb, uint8_t val) 206 { 207 int rv; 208 209 rv = puffs_framebuf_putdata(pb, &val, 1); 210 CHECK(rv == 0); 211 } 212 213 void 214 psbuf_put_2(struct puffs_framebuf *pb, uint16_t val) 215 { 216 int rv; 217 218 HTOBE16(val); 219 rv = puffs_framebuf_putdata(pb, &val, 2); 220 CHECK(rv == 0); 221 } 222 223 void 224 psbuf_put_4(struct puffs_framebuf *pb, uint32_t val) 225 { 226 int rv; 227 228 HTOBE32(val); 229 rv = puffs_framebuf_putdata(pb, &val, 4); 230 CHECK(rv == 0); 231 } 232 233 void 234 psbuf_put_8(struct puffs_framebuf *pb, uint64_t val) 235 { 236 int rv; 237 238 HTOBE64(val); 239 rv = puffs_framebuf_putdata(pb, &val, 8); 240 CHECK(rv == 0); 241 } 242 243 void 244 psbuf_put_data(struct puffs_framebuf *pb, const void *data, uint32_t dlen) 245 { 246 int rv; 247 248 psbuf_put_4(pb, dlen); 249 rv = puffs_framebuf_putdata(pb, data, dlen); 250 CHECK(rv == 0); 251 } 252 253 void 254 psbuf_put_str(struct puffs_framebuf *pb, const char *str) 255 { 256 257 psbuf_put_data(pb, str, strlen(str)); 258 } 259 260 void 261 psbuf_put_vattr(struct puffs_framebuf *pb, const struct vattr *va, 262 const struct psshfs_ctx *pctx) 263 { 264 uint32_t flags; 265 uint32_t theuid = -1, thegid = -1; 266 flags = 0; 267 268 if (va->va_size != (uint64_t)PUFFS_VNOVAL) 269 flags |= SSH_FILEXFER_ATTR_SIZE; 270 if (va->va_uid != (uid_t)PUFFS_VNOVAL) { 271 theuid = va->va_uid; 272 if (pctx->domangleuid && theuid == pctx->myuid) 273 theuid = pctx->mangleuid; 274 flags |= SSH_FILEXFER_ATTR_UIDGID; 275 } 276 if (va->va_gid != (gid_t)PUFFS_VNOVAL) { 277 thegid = va->va_gid; 278 if (pctx->domanglegid && thegid == pctx->mygid) 279 thegid = pctx->manglegid; 280 flags |= SSH_FILEXFER_ATTR_UIDGID; 281 } 282 if (va->va_mode != (mode_t)PUFFS_VNOVAL) 283 flags |= SSH_FILEXFER_ATTR_PERMISSIONS; 284 285 if (va->va_atime.tv_sec != PUFFS_VNOVAL) 286 flags |= SSH_FILEXFER_ATTR_ACCESSTIME; 287 288 psbuf_put_4(pb, flags); 289 if (flags & SSH_FILEXFER_ATTR_SIZE) 290 psbuf_put_8(pb, va->va_size); 291 if (flags & SSH_FILEXFER_ATTR_UIDGID) { 292 psbuf_put_4(pb, theuid); 293 psbuf_put_4(pb, thegid); 294 } 295 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) 296 psbuf_put_4(pb, va->va_mode); 297 298 /* XXX: this is totally wrong for protocol v3, see OpenSSH */ 299 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) { 300 psbuf_put_4(pb, va->va_atime.tv_sec); 301 psbuf_put_4(pb, va->va_mtime.tv_sec); 302 } 303 } 304 305 #define ERETURN(rv) return ((rv) == -1 ? errno : 0) 306 307 int 308 psbuf_get_1(struct puffs_framebuf *pb, uint8_t *val) 309 { 310 311 ERETURN(puffs_framebuf_getdata(pb, val, 1)); 312 } 313 314 int 315 psbuf_get_2(struct puffs_framebuf *pb, uint16_t *val) 316 { 317 int rv; 318 319 rv = puffs_framebuf_getdata(pb, val, 2); 320 BE16TOH(*val); 321 322 ERETURN(rv); 323 } 324 325 int 326 psbuf_get_4(struct puffs_framebuf *pb, uint32_t *val) 327 { 328 int rv; 329 330 rv = puffs_framebuf_getdata(pb, val, 4); 331 BE32TOH(*val); 332 333 ERETURN(rv); 334 } 335 336 int 337 psbuf_get_8(struct puffs_framebuf *pb, uint64_t *val) 338 { 339 int rv; 340 341 rv = puffs_framebuf_getdata(pb, val, 8); 342 BE64TOH(*val); 343 344 ERETURN(rv); 345 } 346 347 int 348 psbuf_get_str(struct puffs_framebuf *pb, char **strp, uint32_t *strlenp) 349 { 350 char *str; 351 uint32_t len; 352 353 FAILRV(psbuf_get_4(pb, &len)); 354 355 if (puffs_framebuf_remaining(pb) < len) 356 return EPROTO; 357 358 str = emalloc(len+1); 359 puffs_framebuf_getdata(pb, str, len); 360 str[len] = '\0'; 361 *strp = str; 362 363 if (strlenp) 364 *strlenp = len; 365 366 return 0; 367 } 368 369 int 370 psbuf_get_vattr(struct puffs_framebuf *pb, struct vattr *vap) 371 { 372 uint32_t flags; 373 uint32_t val; 374 375 puffs_vattr_null(vap); 376 377 FAILRV(psbuf_get_4(pb, &flags)); 378 379 if (flags & SSH_FILEXFER_ATTR_SIZE) { 380 FAILRV(psbuf_get_8(pb, &vap->va_size)); 381 vap->va_bytes = vap->va_size; 382 } 383 if (flags & SSH_FILEXFER_ATTR_UIDGID) { 384 FAILRV(psbuf_get_4(pb, &vap->va_uid)); 385 FAILRV(psbuf_get_4(pb, &vap->va_gid)); 386 } 387 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { 388 FAILRV(psbuf_get_4(pb, &vap->va_mode)); 389 vap->va_type = puffs_mode2vt(vap->va_mode); 390 } 391 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) { 392 /* 393 * XXX: this is utterly wrong if we want to speak 394 * protocol version 3, but it seems like the 395 * "internet standard" for doing this 396 */ 397 FAILRV(psbuf_get_4(pb, &val)); 398 vap->va_atime.tv_sec = val; 399 FAILRV(psbuf_get_4(pb, &val)); 400 vap->va_mtime.tv_sec = val; 401 /* make ctime the same as mtime */ 402 vap->va_ctime.tv_sec = val; 403 404 vap->va_atime.tv_nsec = 0; 405 vap->va_ctime.tv_nsec = 0; 406 vap->va_mtime.tv_nsec = 0; 407 } 408 409 return 0; 410 } 411 412 /* 413 * Buffer content helpers. Caller frees all data. 414 */ 415 416 /* 417 * error mapping.. most are not expected for a file system, but 418 * should help with diagnosing a possible error 419 */ 420 static int emap[] = { 421 0, /* OK */ 422 0, /* EOF */ 423 ENOENT, /* NO_SUCH_FILE */ 424 EPERM, /* PERMISSION_DENIED */ 425 EIO, /* FAILURE */ 426 EBADMSG, /* BAD_MESSAGE */ 427 ENOTCONN, /* NO_CONNECTION */ 428 ECONNRESET, /* CONNECTION_LOST */ 429 EOPNOTSUPP, /* OP_UNSUPPORTED */ 430 EINVAL, /* INVALID_HANDLE */ 431 ENXIO, /* NO_SUCH_PATH */ 432 EEXIST, /* FILE_ALREADY_EXISTS */ 433 ENODEV /* WRITE_PROTECT */ 434 }; 435 #define NERRORS ((int)(sizeof(emap) / sizeof(emap[0]))) 436 437 static int 438 sftperr_to_errno(int error) 439 { 440 441 if (!error) 442 return 0; 443 444 if (error >= NERRORS || error < 0) 445 return EPROTO; 446 447 return emap[error]; 448 } 449 450 #define INVALRESPONSE EPROTO 451 452 static int 453 expectcode(struct puffs_framebuf *pb, int value) 454 { 455 uint32_t error; 456 uint8_t type; 457 458 type = psbuf_get_type(pb); 459 if (type == value) 460 return 0; 461 462 if (type != SSH_FXP_STATUS) 463 return INVALRESPONSE; 464 465 FAILRV(psbuf_get_4(pb, &error)); 466 467 return sftperr_to_errno(error); 468 } 469 470 #define CHECKCODE(pb,val) \ 471 do { \ 472 int rv; \ 473 rv = expectcode(pb, val); \ 474 if (rv) \ 475 return rv; \ 476 } while (/*CONSTCOND*/0) 477 478 int 479 psbuf_expect_status(struct puffs_framebuf *pb) 480 { 481 uint32_t error; 482 483 if (psbuf_get_type(pb) != SSH_FXP_STATUS) 484 return INVALRESPONSE; 485 486 FAILRV(psbuf_get_4(pb, &error)); 487 488 return sftperr_to_errno(error); 489 } 490 491 int 492 psbuf_expect_handle(struct puffs_framebuf *pb, char **hand, uint32_t *handlen) 493 { 494 495 CHECKCODE(pb, SSH_FXP_HANDLE); 496 FAILRV(psbuf_get_str(pb, hand, handlen)); 497 498 return 0; 499 } 500 501 /* no memory allocation, direct copy */ 502 int 503 psbuf_do_data(struct puffs_framebuf *pb, uint8_t *data, uint32_t *dlen) 504 { 505 void *win; 506 size_t bufoff, winlen; 507 uint32_t len, dataoff; 508 509 if (psbuf_get_type(pb) != SSH_FXP_DATA) { 510 uint32_t val; 511 512 if (psbuf_get_type(pb) != SSH_FXP_STATUS) 513 return INVALRESPONSE; 514 515 if (psbuf_get_4(pb, &val) != 0) 516 return INVALRESPONSE; 517 518 if (val != SSH_FX_EOF) 519 return sftperr_to_errno(val); 520 521 *dlen = 0; 522 return 0; 523 } 524 if (psbuf_get_4(pb, &len) != 0) 525 return INVALRESPONSE; 526 527 if (*dlen < len) 528 return EINVAL; 529 530 *dlen = 0; 531 532 dataoff = 0; 533 while (dataoff < len) { 534 winlen = len-dataoff; 535 bufoff = puffs_framebuf_telloff(pb); 536 if (puffs_framebuf_getwindow(pb, bufoff, 537 &win, &winlen) == -1) 538 return EINVAL; 539 if (winlen == 0) 540 break; 541 542 memcpy(data + dataoff, win, winlen); 543 dataoff += winlen; 544 } 545 546 *dlen = dataoff; 547 548 return 0; 549 } 550 551 int 552 psbuf_expect_name(struct puffs_framebuf *pb, uint32_t *count) 553 { 554 555 CHECKCODE(pb, SSH_FXP_NAME); 556 FAILRV(psbuf_get_4(pb, count)); 557 558 return 0; 559 } 560 561 int 562 psbuf_expect_attrs(struct puffs_framebuf *pb, struct vattr *vap) 563 { 564 565 CHECKCODE(pb, SSH_FXP_ATTRS); 566 FAILRV(psbuf_get_vattr(pb, vap)); 567 568 return 0; 569 } 570 571 /* 572 * More helpers: larger-scale put functions 573 */ 574 575 void 576 psbuf_req_data(struct puffs_framebuf *pb, int type, uint32_t reqid, 577 const void *data, uint32_t dlen) 578 { 579 580 psbuf_put_1(pb, type); 581 psbuf_put_4(pb, reqid); 582 psbuf_put_data(pb, data, dlen); 583 } 584 585 void 586 psbuf_req_str(struct puffs_framebuf *pb, int type, uint32_t reqid, 587 const char *str) 588 { 589 590 psbuf_req_data(pb, type, reqid, str, strlen(str)); 591 } 592