chiark / gitweb /
Expunge revision histories.
[shells] / chrootsh.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Chroot gaol shell
6  *
7  * (c) 1999 Mark Wooding
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <syslog.h>
37 #include <pwd.h>
38
39 extern char **environ;
40
41 /*----- Main code ---------------------------------------------------------*/
42
43 #ifndef CHROOTSH_PATH
44 #  define CHROOTSH_PATH "/usr/bin/chrootsh"
45 #endif
46
47 static const char *quis = "chrootsh";
48
49 static void *xmalloc(size_t sz)
50 {
51   void *p = malloc(sz);
52   if (!p) {
53     setuid(getuid());
54     fprintf(stderr, "%s: not enough memory\n", quis);
55     exit(EXIT_FAILURE);
56   }
57   return (p);
58 }
59
60 static char *xstrdup(const char *p)
61 {
62   size_t sz = strlen(p) + 1;
63   char *q = xmalloc(sz);
64   memcpy(q, p, sz);
65   return (q);
66 }
67
68 int main(int argc, char *argv[])
69 {
70   struct passwd *pw;
71   uid_t me = getuid();
72   char *myname;
73   char **env;
74   char **av;
75
76   /* --- Resolve the program name --- */
77
78   {
79     char *p, *q;
80     p = argv[0];
81     for (q = argv[0]; *q; q++) {
82       if (*q == '/')
83         p = q + 1;
84     }
85     if (*p == '-')
86       p++;
87     quis = p;
88     openlog(quis, LOG_PID | LOG_NDELAY, LOG_DAEMON);
89   }
90
91   /* --- Check the user is meant to be chrooted --- */
92
93   {
94     uid_t eff = geteuid();
95
96     setreuid(eff, me);
97     pw = getpwuid(me);
98     if (!pw) {
99       syslog(LOG_ERR, "executed by non-existant user (uid = %i)", (int)me);
100       fprintf(stderr, "%s: you don't exist.  Go away.\n", quis);
101       exit(EXIT_FAILURE);
102     }
103     if (strcmp(pw->pw_shell, CHROOTSH_PATH) != 0) {
104       syslog(LOG_ERR, "executed by non-chrooted user `%s'", pw->pw_name);
105       fprintf(stderr, "%s: you aren't a chrooted user\n", quis);
106       exit(EXIT_FAILURE);
107     }
108     endpwent();
109     setreuid(me, eff);
110   }
111
112   /* --- Chroot the user --- */
113
114   {
115     char *p = xstrdup(pw->pw_dir);
116     char *q = strstr(p, "/./");
117     if (q)
118       *q = 0;
119     
120     if (chdir(p) || chroot(p)) {
121       int e = errno;
122       syslog(LOG_ERR, "error entering chroot gaol: %m");
123       setuid(me);
124       fprintf(stderr, "%s: couldn't call chroot: %s\n", quis, strerror(e));
125       exit(EXIT_FAILURE);
126     }
127     setuid(me);
128     free(p);
129   }
130
131   /* --- Read the new password block --- */
132
133   myname = xstrdup(pw->pw_name);
134   pw = getpwnam(myname);
135   if (!pw) {
136     syslog(LOG_ERR,
137            "configuration error: user `%s' not defined in gaol", myname);
138     fprintf(stderr, "%s: you don't exist in the gaol\n", quis);
139     exit(EXIT_FAILURE);
140   }
141   endpwent();
142
143   /* --- Now fiddle with environment strings and suchlike --- */
144
145   {
146     size_t n;
147     char **homevar = 0;
148     for (n = 0; environ[n]; n++)
149       ;
150     env = xmalloc((n + 1) * sizeof(char *));
151
152     for (n = 0; environ[n]; n++) {
153       if (strncmp(environ[n], "HOME=", 5) == 0) {
154         char *p = xmalloc(6 + strlen(pw->pw_dir));
155         sprintf(p, "HOME=%s", pw->pw_dir);
156         homevar = &env[n];
157         env[n] = p;
158       } else if (strncmp(environ[n], "SHELL=", 6) == 0) {
159         char *p = xmalloc(7 + strlen(pw->pw_shell));
160         sprintf(p, "SHELL=%s", pw->pw_shell);
161         env[n] = p;
162       } else
163         env[n] = environ[n];
164     }
165     env[n] = 0;
166
167     /* --- Change directory (again) --- */
168
169     if (chdir(pw->pw_dir)) {
170       if (homevar) {
171         free(*homevar);
172         *homevar = "HOME=/";
173       }
174       fprintf(stderr, "No directory, logging in with HOME=/\n");
175     }
176   }
177
178   /* --- Finally, sort the argument list --- */
179
180   {
181     char *p, *q;
182     int i;
183
184     av = xmalloc((argc + 1) * sizeof(char *));
185     p = pw->pw_shell;
186     for (q = p; *q; q++) {
187       if (*q == '/')
188         p = q + 1;
189     }
190     if (argv[0][0] == '-') {
191       q = xmalloc(2 + strlen(p));
192       *q = '-';
193       strcpy(q + 1, p);
194       av[0] = q;
195     } else
196       av[0] = p;
197
198     for (i = 1; i <= argc; i++)
199       av[i] = argv[i];
200   }
201
202   /* --- Run the real shell --- */
203
204   syslog(LOG_INFO, "chroot user `%s' logged in ok", myname);
205   closelog();
206   execve(pw->pw_shell, av, env);
207   fprintf(stderr, "%s: couldn't exec `%s': %s\n",
208           quis, pw->pw_shell, strerror(errno));
209   return (EXIT_FAILURE);
210 }
211
212 /*----- That's all, folks -------------------------------------------------*/