chiark / gitweb /
Fix documentation for `ldconfig' command. Whoops.
[shells] / chrootsh.c
1 /* -*-c-*-
2  *
3  * $Id: chrootsh.c,v 1.4 1999/04/22 00:30:27 mdw Exp $
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 /*----- Revision history --------------------------------------------------* 
28  *
29  * $Log: chrootsh.c,v $
30  * Revision 1.4  1999/04/22 00:30:27  mdw
31  * Miscellanous tidying and security fixes.  Lots of thanks due to Clive
32  * Jones.
33  *
34  * Revision 1.3  1999/04/21 22:52:43  mdw
35  * Added a pile of syslog stuff, so that admins can see what this thing is
36  * doing.
37  *
38  * Revision 1.2  1999/04/21 09:07:55  mdw
39  * Fiddle with copyright messages so that they're correct.
40  *
41  * Revision 1.1.1.1  1999/04/20 00:19:04  mdw
42  * Initial versions.
43  *
44  */
45
46 /*----- Header files ------------------------------------------------------*/
47
48 #include <errno.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include <sys/types.h>
54 #include <unistd.h>
55 #include <syslog.h>
56 #include <pwd.h>
57
58 extern char **environ;
59
60 /*----- Main code ---------------------------------------------------------*/
61
62 #ifndef CHROOTSH_PATH
63 #  define CHROOTSH_PATH "/usr/bin/chrootsh"
64 #endif
65
66 static const char *quis = "chrootsh";
67
68 static void *xmalloc(size_t sz)
69 {
70   void *p = malloc(sz);
71   if (!p) {
72     setuid(getuid());
73     fprintf(stderr, "%s: not enough memory\n", quis);
74     exit(EXIT_FAILURE);
75   }
76   return (p);
77 }
78
79 static char *xstrdup(const char *p)
80 {
81   size_t sz = strlen(p) + 1;
82   char *q = xmalloc(sz);
83   memcpy(q, p, sz);
84   return (q);
85 }
86
87 int main(int argc, char *argv[])
88 {
89   struct passwd *pw;
90   uid_t me = getuid();
91   char *myname;
92   char **env;
93   char **av;
94
95   /* --- Resolve the program name --- */
96
97   {
98     char *p, *q;
99     p = argv[0];
100     for (q = argv[0]; *q; q++) {
101       if (*q == '/')
102         p = q + 1;
103     }
104     if (*p == '-')
105       p++;
106     quis = p;
107     openlog(quis, LOG_PID | LOG_NDELAY, LOG_DAEMON);
108   }
109
110   /* --- Check the user is meant to be chrooted --- */
111
112   {
113     uid_t eff = geteuid();
114
115     setreuid(eff, me);
116     pw = getpwuid(me);
117     if (!pw) {
118       syslog(LOG_ERR, "executed by non-existant user (uid = %i)", (int)me);
119       fprintf(stderr, "%s: you don't exist.  Go away.\n", quis);
120       exit(EXIT_FAILURE);
121     }
122     if (strcmp(pw->pw_shell, CHROOTSH_PATH) != 0) {
123       syslog(LOG_ERR, "executed by non-chrooted user `%s'", pw->pw_name);
124       fprintf(stderr, "%s: you aren't a chrooted user\n", quis);
125       exit(EXIT_FAILURE);
126     }
127     endpwent();
128     setreuid(me, eff);
129   }
130
131   /* --- Chroot the user --- */
132
133   {
134     char *p = xstrdup(pw->pw_dir);
135     char *q = strstr(p, "/./");
136     if (q)
137       *q = 0;
138     
139     if (chdir(p) || chroot(p)) {
140       int e = errno;
141       syslog(LOG_ERR, "error entering chroot gaol: %m");
142       setuid(me);
143       fprintf(stderr, "%s: couldn't call chroot: %s\n", quis, strerror(e));
144       exit(EXIT_FAILURE);
145     }
146     setuid(me);
147     free(p);
148   }
149
150   /* --- Read the new password block --- */
151
152   myname = xstrdup(pw->pw_name);
153   pw = getpwnam(myname);
154   if (!pw) {
155     syslog(LOG_ERR,
156            "configuration error: user `%s' not defined in gaol", myname);
157     fprintf(stderr, "%s: you don't exist in the gaol\n", quis);
158     exit(EXIT_FAILURE);
159   }
160   endpwent();
161
162   /* --- Now fiddle with environment strings and suchlike --- */
163
164   {
165     size_t n;
166     char **homevar = 0;
167     for (n = 0; environ[n]; n++)
168       ;
169     env = xmalloc((n + 1) * sizeof(char *));
170
171     for (n = 0; environ[n]; n++) {
172       if (strncmp(environ[n], "HOME=", 5) == 0) {
173         char *p = xmalloc(6 + strlen(pw->pw_dir));
174         sprintf(p, "HOME=%s", pw->pw_dir);
175         homevar = &env[n];
176         env[n] = p;
177       } else if (strncmp(environ[n], "SHELL=", 6) == 0) {
178         char *p = xmalloc(7 + strlen(pw->pw_shell));
179         sprintf(p, "SHELL=%s", pw->pw_shell);
180         env[n] = p;
181       } else
182         env[n] = environ[n];
183     }
184     env[n] = 0;
185
186     /* --- Change directory (again) --- */
187
188     if (chdir(pw->pw_dir)) {
189       if (homevar) {
190         free(*homevar);
191         *homevar = "HOME=/";
192       }
193       fprintf(stderr, "No directory, logging in with HOME=/\n");
194     }
195   }
196
197   /* --- Finally, sort the argument list --- */
198
199   {
200     char *p, *q;
201     int i;
202
203     av = xmalloc((argc + 1) * sizeof(char *));
204     p = pw->pw_shell;
205     for (q = p; *q; q++) {
206       if (*q == '/')
207         p = q + 1;
208     }
209     if (argv[0][0] == '-') {
210       q = xmalloc(2 + strlen(p));
211       *q = '-';
212       strcpy(q + 1, p);
213       av[0] = q;
214     } else
215       av[0] = p;
216
217     for (i = 1; i <= argc; i++)
218       av[i] = argv[i];
219   }
220
221   /* --- Run the real shell --- */
222
223   syslog(LOG_INFO, "chroot user `%s' logged in ok", myname);
224   closelog();
225   execve(pw->pw_shell, av, env);
226   fprintf(stderr, "%s: couldn't exec `%s': %s\n",
227           quis, pw->pw_shell, strerror(errno));
228   return (EXIT_FAILURE);
229 }
230
231 /*----- That's all, folks -------------------------------------------------*/