/ libpkg / ssh.c
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  }