1 /* $MirOS: src/libexec/anoncvssh/anoncvssh.c,v 1.9 2008/06/07 20:25:43 tg Exp $ */
2 
3 /*-
4  * Copyright (c) 2004, 2005, 2006, 2008
5  *	Thorsten "mirabilos" Glaser <tg@mirbsd.org>
6  *
7  * Provided that these terms and disclaimer and all copyright notices
8  * are retained or reproduced in an accompanying document, permission
9  * is granted to deal in this work without restriction, including un-
10  * limited rights to use, publicly perform, distribute, sell, modify,
11  * merge, give away, or sublicence.
12  *
13  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
14  * the utmost extent permitted by applicable law, neither express nor
15  * implied; without malicious intent or gross negligence. In no event
16  * may a licensor, author or contributor be held liable for indirect,
17  * direct, other damage, loss, or other issues arising in any way out
18  * of dealing in the work, even if advised of the possibility of such
19  * damage or existence of a defect, except proven that it results out
20  * of said person's immediate fault when using the work as intended.
21  *-
22  * user shell to be used for chrooted access (anonymous or personali-
23  * sed, read-only or read-write) to cvs and possibly rsync.
24  * This programme requires ANSI C.
25  */
26 
27 /*
28  * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
29  * Copyright (c) 1997 Bob Beck <beck@obtuse.com>
30  * Copyright (c) 1996 Thorsten Lockert <tholo@sigmasoft.com>
31  *
32  * Permission to use, copy, modify, and distribute this software for any
33  * purpose with or without fee is hereby granted, provided that the above
34  * copyright notice and this permission notice appear in all copies.
35  *
36  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
37  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
38  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
39  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
41  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
42  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43  */
44 
45 #include <sys/types.h>
46 #include <sys/time.h>
47 #include <sys/resource.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
51 #include <paths.h>
52 #endif
53 #include <pwd.h>
54 #include <unistd.h>
55 #include <errno.h>
56 
57 /*
58  * You may need to change this path to ensure that RCS, CVS and diff
59  * can be found
60  */
61 #ifndef _PATH_DEFPATH
62 #define _PATH_DEFPATH	"/bin"
63 #endif
64 
65 /*
66  * This should not normally have to be changed
67  */
68 #ifndef _PATH_BSHELL
69 #define _PATH_BSHELL	"/bin/sh"
70 #endif
71 
72 /*
73  * Location of CVS tree, relative to the anonymous CVS user's
74  * home directory
75  */
76 #ifndef LOCALROOT
77 #define LOCALROOT	"/cvs"
78 #endif
79 
80 /*
81  * Account and host name to be used when accessing the
82  * CVS repository remotely
83  */
84 #ifndef HOSTNAME
85 #if !defined(FQDN) || !defined(ANONCVS_USER)
86 #undef FQDN
87 #undef ANONCVS_USER
88 #define ANONCVS_USER	"_anoncvs"
89 #define FQDN		"mirbsd.org"
90 #endif
91 #define HOSTNAME	ANONCVS_USER "@" FQDN
92 #endif /* ndef HOSTNAME */
93 
94 /*
95  * This is our own programme name
96  */
97 #ifndef ANONCVSSH_NAME
98 #define ANONCVSSH_NAME	"anoncvssh"
99 #endif
100 
101 /*
102  * This is the rsync server to invoke
103  */
104 #define FULL_RSYNC	"rsync --server "
105 
106 /*
107  * Niceness increase
108  */
109 #ifndef NICE_INC
110 #define NICE_INC	5
111 #endif
112 #if NICE_INC < 0
113 #error "may not decrease niceness"
114 #endif
115 
116 
117 /****************************************************************/
118 
119 static const char progID[] = "@(#) " HOSTNAME ":" LOCALROOT
120     "\n@(#) $MirOS: src/libexec/anoncvssh/anoncvssh.c,v 1.9 2008/06/07 20:25:43 tg Exp $";
121 
122 #ifdef USE_SYSLOG
123 #include <string.h>
124 #include <syslog.h>
125 #include <netinet/in.h>
126 #include <sys/socket.h>
127 #include <arpa/inet.h>
128 #define LOG_FACILITY	LOG_DAEMON
129 #define LOG_PRIO	LOG_INFO
130 #define DO_LOG(x, ...)	syslog(LOG_NOTICE, x, ## __VA_ARGS__)
131 #define DO_LOG0(x)	syslog(LOG_NOTICE, x)
132 #else /* def USE_SYSLOG */
133 #define DO_LOG(x, ...)	/* nothing */
134 #define DO_LOG0(x)	/* nothing */
135 #endif /* ! def USE_SYSLOG */
136 
137 int main(int, char *[]);
138 
139 const char * const env[] = {
140 	"PATH=" _PATH_DEFPATH,
141 	"SHELL=" _PATH_BSHELL,
142 	"CVSROOT=" LOCALROOT,
143 	"HOME=/",
144 #ifndef ACCESS_READWRITE
145 	"CVSREADONLYFS=1",
146 #endif
147 	NULL
148 };
149 
150 #ifdef ANONUSERLIST
151 const char * const anonusers[] = {
152 #ifdef ANONCVS_USER
153 	ANONCVS_USER,
154 #endif
155 	ANONUSERLIST,
156 	NULL
157 };
158 #endif
159 
160 int
main(int argc,char * argv[])161 main(int argc, char *argv[])
162 {
163 	struct passwd *pw;
164 	int niceness;
165 	char *chrootdir, *pgm;
166 #ifdef ANONUSERLIST
167 	const char * const *ccpp;
168 #endif
169 #ifdef CHROOT_PARENT_DIR
170 	char *s;
171 #endif
172 #ifdef DEBUG
173 	int i;
174 #endif
175 
176 	pw = getpwuid(getuid());
177 	if (pw == NULL) {
178 		fprintf(stderr, "no user for uid %d\n", getuid());
179 		exit(1);
180 	}
181 	if (pw->pw_dir == NULL) {
182 		fprintf(stderr, "no home directory\n");
183 		exit(1);
184 	}
185 
186 #ifdef USE_SYSLOG
187 	openlog(ANONCVSSH_NAME, LOG_PID | LOG_NDELAY, LOG_FACILITY);
188 #endif
189 
190 	/* drop priority by NICE_INC; anoncvs is a background process */
191 	errno = 0;
192 	niceness = NICE_INC + getpriority(PRIO_PROCESS, 0);
193 	if ((niceness == (NICE_INC - 1)) && errno) {
194 		DO_LOG0("Can't get process priority!");
195 	} else if (setpriority(PRIO_PROCESS, 0, niceness)) {
196 		DO_LOG0("Can't set process priority!");
197 	}
198 
199 #if defined(ANONCVS_USER) || defined(ANONUSERLIST)
200 	/*
201 	 * I love lusers who have to test every setuid binary on my machine.
202 	 */
203 	if (getuid() == 0)
204 		goto user_ok;
205 
206 #ifdef ANONUSERLIST
207 	for (ccpp = anonusers; *ccpp; ++ccpp)
208 		if (!strcmp(pw->pw_name, *ccpp))
209 			goto user_ok;
210 #else
211 	if (!strcmp(pw->pw_name, ANONCVS_USER))
212 		goto user_ok;
213 #endif
214 
215 	fprintf(stderr, "You're not supposed to be running me!\n");
216 #ifdef USE_SYSLOG
217 	syslog(LOG_NOTICE, "User %s(%d) invoked %s - Possible twink?",
218 	    pw->pw_name, pw->pw_uid, ANONCVSSH_NAME);
219 #endif /* def USE_SYSLOG */
220 	exit(1);
221 
222  user_ok:
223 #endif /* def ANONCVS_USER || def ANONUSERLIST */
224 
225 	setuid(0);
226 	if ((chrootdir = strdup(pw->pw_dir)) == NULL) {
227 		perror("strdup");
228 		exit(1);
229 	}
230 #ifdef CHROOT_PARENT_DIR
231 	if ((s = strrchr(chrootdir, '/')) == NULL) {
232 		DO_LOG0("No slash in user's home directory!\n");
233 		exit(1);
234 	}
235 	*s = '\0';
236 	if (strrchr(chrootdir, '/') == NULL) {
237 		DO_LOG0("No slash in user's parent directory!\n");
238 		exit(1);
239 	}
240 #endif
241 	if (chroot(chrootdir) == -1) {
242 		perror("chroot");
243 		exit(1);
244 	}
245 	chdir("/");
246 	setuid(pw->pw_uid);
247 	free(chrootdir);
248 
249 	/*
250 	 * programme now "safe"
251 	 */
252 
253 
254 #ifdef ACCESS_PSERVER
255 	/* If we want pserver functionality (NOT! It's braindead!) */
256 	if ((argc == 2) && (strcmp("pserver", argv[1]) == 0)) {
257 #ifdef USE_SYSLOG
258  	        int slen;
259 		struct sockaddr_in my_sa, peer_sa;
260 		char *us, *them;
261 
262 		slen = sizeof(my_sa);
263 		if (getsockname(0, (struct sockaddr *) &my_sa, &slen) != 0) {
264 			perror("getsockname");
265 			exit(1);
266 		}
267 		us = strdup(inet_ntoa(my_sa.sin_addr));
268 		if (us == NULL) {
269 			fprintf(stderr, "malloc failed\n");
270 			exit(1);
271 		}
272 		slen = sizeof(peer_sa);
273 		if (getpeername(0, (struct sockaddr *) &peer_sa, &slen) != 0) {
274 			perror("getpeername");
275 			exit(1);
276 		}
277 		them = strdup(inet_ntoa(peer_sa.sin_addr));
278 		if (them == NULL) {
279 			fprintf(stderr, "malloc failed\n");
280 			exit(1);
281 		}
282 	        syslog(LOG_PRIO,
283 		    "pserver connection from %s:%d to %s:%d\n",
284 		    them, ntohs(peer_sa.sin_port),
285 		    us, ntohs(my_sa.sin_port));
286 #endif /* def USE_SYSLOG */
287 		execle("/bin/cvs", "cvs",
288 		    "--allow-root=" LOCALROOT, "pserver", NULL, env);
289 		perror("execle: cvs");
290 		fprintf(stderr, "unable to exec CVS pserver!\n");
291 		exit(1);
292 		/* NOTREACHED */
293 	}
294 #endif /* def ACCESS_PSERVER */
295 
296 	if ((argc == 3) && (!strcmp(ANONCVSSH_NAME, argv[0])) &&
297 	    (!strcmp("-c", argv[1]))) {
298 		pgm = argv[2];
299 		if (!strncmp("/usr/bin/", pgm, strlen("/usr/bin/")))
300 			pgm += strlen("/usr/bin/");
301 		if (!strncmp("/bin/", pgm, strlen("/bin/")))
302 			pgm += strlen("/bin/");
303 		if ((!strcmp("cvs server", pgm)) ||
304 		    (!strcmp("cvs -R server", pgm)) ||
305 		    (!strcmp("cvs -d " LOCALROOT " server", pgm))) {
306 			execle("/bin/cvs", "cvs", "server", NULL, env);
307 			perror("execle: cvs");
308 			DO_LOG0("chaining to CVS failed!");
309 			fprintf(stderr, "unable to exec CVS server!\n");
310 			exit(1);
311 			/* NOTREACHED */
312 		} else if (!strncmp(FULL_RSYNC, pgm, strlen(FULL_RSYNC))) {
313 #ifdef ACCESS_RSYNC
314 			int i = -1;
315 			char *newarg[256];
316 			char *p = pgm;
317 
318 		lp:
319 			newarg[++i] = strsep(&p, " ");
320 			if ((newarg[i] != NULL) && (i < 255))
321 				goto lp;
322 #ifdef DEBUG
323 			argc = i;
324 			DO_LOG("calling rsync; argc = %d\n", argc);
325 			for (i = 0; i < argc; i++)
326 				DO_LOG("newarg[%d] = \"%s\"\n", i, newarg[i]);
327 #endif
328 			execve("/bin/rsync", newarg, (char **)env);
329 			perror("execve: rsync");
330 			DO_LOG0("chaining to rsync failed!");
331 			fprintf(stderr, "unable to exec RSYNC!\n");
332 			exit(1);
333 			/* NOTREACHED */
334 #else
335 			DO_LOG0("access to RSYNC prohibited!");
336 #endif
337 		}
338 	}
339 
340 	DO_LOG0("parameter failure, printing help message");
341 	fprintf(stderr, "\n"
342 	    "To use anonymous CVS or RSYNC, install the latest version of the\n"
343 	    "client access software, as well as OpenSSH, on your computer.\n"
344 	    "Then, set your CVSROOT environment variable to the following\n"
345 	    "value for CVS access:\n\t%s:%s\n"
346 	    "For RSYNC, use SSH instead of RSH.\n",
347 	    HOSTNAME, LOCALROOT);
348 #ifdef DEBUG
349 	DO_LOG("argc = %d\n", argc);
350 	for (i = 0; i < argc; i++)
351 		DO_LOG("argv[%d] = \"%s\"\n", i, argv[i]);
352 #endif
353 	sleep(10);
354 	exit(0);
355 }
356