ssh.c
1 /*- 2 * Copyright (c) 2013-2014 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org> 4 * 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 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifdef HAVE_CONFIG_H 29 #include "pkg_config.h" 30 #endif 31 #if __has_include(<sys/capsicum.h>) 32 #define HAVE_CAPSICUM 1 33 #include <sys/capsicum.h> 34 #endif 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/stat.h> 38 39 #include <ctype.h> 40 #include <stdlib.h> 41 #include <inttypes.h> 42 #include <stdio.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <fcntl.h> 46 47 #include <bsd_compat.h> 48 49 #include "pkg.h" 50 #include "private/event.h" 51 #include "private/utils.h" 52 53 int 54 pkg_sshserve(int fd) 55 { 56 struct stat st; 57 char *line = NULL; 58 char *file, *age; 59 size_t linecap = 0, r; 60 ssize_t linelen; 61 time_t mtime = 0; 62 const char *errstr; 63 int ffd; 64 char buf[32768]; 65 char fpath[MAXPATHLEN]; 66 char rpath[MAXPATHLEN]; 67 const char *restricted = NULL; 68 69 restricted = pkg_object_string(pkg_config_get("SSH_RESTRICT_DIR")); 70 71 printf("ok: pkg "PKGVERSION"\n"); 72 for (;;) { 73 if ((linelen = getline(&line, &linecap, stdin)) < 0) 74 break; 75 76 if (linelen == 0) 77 continue; 78 79 /* trim cr */ 80 if (line[linelen - 1] == '\n') 81 line[linelen - 1] = '\0'; 82 83 if (STREQ(line, "quit")) { 84 free(line); 85 return (EPKG_OK); 86 } 87 88 if (strncmp(line, "get ", 4) != 0) { 89 printf("ko: unknown command '%s'\n", line); 90 continue; 91 } 92 93 file = line + 4; 94 95 if (*file == '\0') { 96 printf("ko: bad command get, expecting 'get file age'\n"); 97 continue; 98 } 99 if (*file == '/') 100 file++; 101 102 pkg_debug(1, "SSH server> file requested: %s", file); 103 104 age = file; 105 while (!isspace(*age)) { 106 if (*age == '\0') { 107 age = NULL; 108 break; 109 } 110 age++; 111 } 112 113 if (age == NULL) { 114 printf("ko: bad command get, expecting 'get file age'\n"); 115 continue; 116 } 117 118 *age = '\0'; 119 age++; 120 121 while (isspace(*age)) { 122 if (*age == '\0') { 123 age = NULL; 124 break; 125 } 126 age++; 127 } 128 129 if (age == NULL) { 130 printf("ko: bad command get, expecting 'get file age'\n"); 131 continue; 132 } 133 134 mtime = strtonum(age, 0, LONG_MAX, &errstr); 135 if (errstr) { 136 printf("ko: bad number %s: %s\n", age, errstr); 137 continue; 138 } 139 140 #ifdef HAVE_CAPSICUM 141 if (!cap_sandboxed() && restricted != NULL) { 142 #else 143 if (restricted != NULL) { 144 #endif 145 if (chdir(restricted)) { 146 printf("ko: chdir failed (%s)\n", restricted); 147 continue; 148 } 149 150 if (realpath(file, fpath) == NULL || 151 realpath(restricted, rpath) == NULL || 152 strncmp(rpath, get_dirname(fpath), strlen(rpath)) != 0) { 153 printf("ko: file not found\n"); 154 continue; 155 } 156 } 157 158 if (fstatat(fd, file, &st, 0) == -1) { 159 pkg_debug(1, "SSH server> fstatat failed"); 160 printf("ko: file not found\n"); 161 continue; 162 } 163 164 if (!S_ISREG(st.st_mode)) { 165 printf("ko: not a file\n"); 166 continue; 167 } 168 169 if (st.st_mtime <= mtime) { 170 printf("ok: 0\n"); 171 continue; 172 } 173 174 if ((ffd = openat(fd, file, O_RDONLY)) == -1) { 175 printf("ko: file not found\n"); 176 continue; 177 } 178 179 printf("ok: %" PRIdMAX "\n", (intmax_t)st.st_size); 180 pkg_debug(1, "SSH server> sending ok: %" PRIdMAX "", (intmax_t)st.st_size); 181 182 while ((r = read(ffd, buf, sizeof(buf))) > 0) { 183 pkg_debug(1, "SSH server> sending data"); 184 fwrite(buf, 1, r, stdout); 185 } 186 187 pkg_debug(1, "SSH server> finished"); 188 189 close(ffd); 190 } 191 192 free(line); 193 194 return (EPKG_OK); 195 }