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