Whamcloud - gitweb
b=8654
[fs/lustre-release.git] / lustre / utils / lacl_upcall.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (C) 2005 Cluster File Systems, Inc.
5  *
6  *   This file is part of Lustre, http://www.lustre.org.
7  *
8  *   Lustre is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   Lustre is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with Lustre; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <stdlib.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <syslog.h>
35
36 #include <liblustre.h>
37 #include <linux/lustre_idl.h>
38 #include <linux/obd.h>
39 #include <linux/lustre_mds.h>
40 #include <linux/obd_support.h>
41
42 #include <portals/ptlctl.h>
43 #include <portals/types.h>
44
45 static int g_testing = 0;
46
47 #define log_msg(fmt, args...)                           \
48         {                                               \
49                 if (g_testing)                          \
50                         printf(fmt, ## args);           \
51                 else                                    \
52                         syslog(LOG_ERR, fmt, ## args);  \
53         }
54
55 int switch_user_identity(uid_t uid)
56 {
57         gid_t           gid;
58         struct passwd  *pw;
59         int             maxgroups, ngroups = 0;
60         gid_t          *groups;
61         struct group   *gr;
62         int             i;
63
64         /* originally must be root */
65         if (getuid() != 0 || geteuid() != 0) {
66                 log_msg("non-root: %u/%u\n", getuid(), geteuid());
67                 return -EPERM;
68         }
69
70         /* nothing more is needed for root */
71         if (uid == 0)
72                 return 0;
73
74         /* - groups
75          * - gid
76          * - uid
77          */
78         maxgroups = sysconf(_SC_NGROUPS_MAX);
79         groups = malloc(maxgroups * sizeof(gid_t));
80         if (!groups) {
81                 log_msg("memory alloc failure\n");
82                 return -ENOMEM;
83         }
84
85         pw = getpwuid(uid);
86         if (!pw) {
87                 log_msg("no such uid %u\n", uid);
88                 return -EPERM;
89         }
90
91         gid = pw->pw_gid;
92
93         while ((gr = getgrent())) {
94                 if (!gr->gr_mem)
95                         continue;
96                 for (i = 0; gr->gr_mem[i]; i++) {
97                         if (strcmp(gr->gr_mem[i], pw->pw_name))
98                                 continue;
99                         groups[ngroups++] = gr->gr_gid;
100                         break;
101                 }
102                 if (ngroups == maxgroups)
103                         break;
104         }
105         endgrent();
106
107         if (setgroups(ngroups, groups) == -1) {
108                 log_msg("set %d groups: %s\n", ngroups, strerror(errno));
109                 free(groups);
110                 return -EPERM;
111         }
112         free(groups);
113
114         if (setgid(gid) == -1) {
115                 log_msg("setgid %u: %s\n", gid, strerror(errno));
116                 return -EPERM;
117         }
118
119         if (setuid(uid) == -1) {
120                 log_msg("setuid %u: %s\n", uid, strerror(errno));
121                 return -EPERM;
122         }
123
124         return 0;
125 }
126
127 /*
128  * caller guarantee args not empty
129  */
130 int compose_command_line(char *cmdline, char *op, char *args)
131 {
132         char *p, *params, *file;
133
134         /* skip the white space at the tail */
135         p = args + strlen(args) - 1;
136
137         while (p >= args) {
138                 if (*p != ' ' && *p != '\t')
139                         break;
140                 p--;
141         }
142
143         /* not allow empty args */
144         if (p < args)
145                 return -1;
146
147         *(p + 1) = '\0';
148
149         /* find next space */
150         while (p >= args) {
151                 if (*p == ' ' || *p == '\t')
152                         break;
153                 p--;
154         }
155
156         if (p >= args) {
157                 *p = '\0';
158                 file = p + 1; /* file name */
159                 params = args;
160         } else {
161                 file = args;
162                 params = "";
163         }
164
165         /* backward path not allowed */
166         if (strstr(file, ".."))
167                 return -EPERM;
168
169         /* absolute path not allowed */
170         if (file[0] == '/')
171                 return -EPERM;
172
173         snprintf(cmdline, PATH_MAX, "%sfacl %s %s",
174                  op, params, file);
175         return 0;
176 }
177
178 void do_acl_command(uid_t uid, char *lroot, char *cmdline)
179 {
180         if (switch_user_identity(uid)) {
181                 printf("MDS: invalid user %u\n", uid);
182                 return;
183         }
184
185         if (chdir(lroot) < 0) {
186                 log_msg("chdir to %s: %s\n", lroot, strerror(errno));
187                 printf("MDS: can't change dir\n");
188                 return;
189         }
190
191         execl("/bin/sh", "sh", "-c", cmdline, NULL);
192         printf("MDS: can't execute\n");
193 }
194
195 #define ERRSTR_NO_CMDLINE       "No command line supplied\n"
196 #define ERRSTR_INVALID_ARGS     "Invalid arguments\n"
197 #define ERRSTR_MDS_PROCESS      "MDS procession error\n"
198
199 /*
200  * The args passed in are:
201  * 1. key (in hex)
202  * 2. uid (in uint)
203  * 3. lustre root
204  * 4. get/set
205  * 5. command line
206  */
207 #define OUTPUT_BUFSIZE          8192
208 int main (int argc, char **argv)
209 {
210         struct   rmtacl_downcall_args dc_args;
211         char    *dc_name = "/proc/fs/lustre/mds/lacl_downcall";
212         int      dc_fd;
213         int      uid;
214         char     output[OUTPUT_BUFSIZE];
215         char     cmdline[PATH_MAX];
216         int      pipeout[2], pipeerr[2], pid;
217         int      output_size, rd, childret;
218
219         if (argc != 6) {
220                 log_msg("invalid argc %d\n", argc);
221                 return -1;
222         }
223
224         /* XXX temp for debugging */
225         log_msg("enter: %s %s %s %s %s\n",
226                 argv[1], argv[2], argv[3], argv[4], argv[5]);
227
228         if (strcmp(argv[4], "get") && strcmp(argv[4], "set")) {
229                 log_msg("invalid arg 4: %s\n", argv[4]);
230                 return -1;
231         }
232
233         dc_args.key = strtoull(argv[1], NULL, 16);
234         dc_args.res = output;
235         dc_args.reslen = 0;
236         dc_args.status = -1; /* default return error */
237
238         uid = atoi(argv[2]);
239
240         if (strlen(argv[5]) == 0) {
241                 dc_args.reslen = sizeof(ERRSTR_NO_CMDLINE);
242                 memcpy(output, ERRSTR_NO_CMDLINE, dc_args.reslen);
243                 goto downcall;
244         }
245
246         if (compose_command_line(cmdline, argv[4], argv[5])) {
247                 dc_args.reslen = sizeof(ERRSTR_INVALID_ARGS);
248                 memcpy(output, ERRSTR_INVALID_ARGS, dc_args.reslen);
249                 goto downcall;
250         }
251
252         /* create pipe */
253         if (pipe(pipeout) < 0 || pipe(pipeerr) < 0) {
254                 dc_args.reslen = sizeof(ERRSTR_MDS_PROCESS);
255                 memcpy(output, ERRSTR_MDS_PROCESS, dc_args.reslen);
256                 goto downcall;
257         }
258
259         if ((pid = fork()) < 0) {
260                 dc_args.reslen = sizeof(ERRSTR_MDS_PROCESS);
261                 memcpy(output, ERRSTR_MDS_PROCESS, dc_args.reslen);
262                 goto downcall;
263         } else if (pid == 0) {
264                 close(pipeout[0]);
265                 if (pipeout[1] != STDOUT_FILENO) {
266                         dup2(pipeout[1], STDOUT_FILENO);
267                         close(pipeout[1]);
268                 }
269
270                 close(pipeerr[0]);
271                 if (pipeerr[1] != STDERR_FILENO) {
272                         dup2(pipeerr[1], STDERR_FILENO);
273                         close(pipeerr[1]);
274                 }
275
276                 close(STDIN_FILENO);
277
278                 do_acl_command(uid, argv[3], cmdline);
279                 exit(-1);
280         }
281
282         /* parent process handling */
283         close(pipeout[1]);
284         close(pipeerr[1]);
285
286         output[0] = 0;
287         output_size = 0;
288         while (1) {
289                 rd = read(pipeout[0], output + output_size,
290                           OUTPUT_BUFSIZE - output_size);
291                 if (rd < 0) {
292                         output_size = sizeof(ERRSTR_MDS_PROCESS);
293                         memcpy(output, ERRSTR_MDS_PROCESS, dc_args.reslen);
294                         break;
295                 }
296                 if (rd == 0)
297                         break;
298                 output_size += rd;
299                 if (output_size >= OUTPUT_BUFSIZE)
300                         break;
301         }
302
303         /* if we got standard output, just leave; otherwise collect
304          * error output.
305          */
306         if (output_size != 0)
307                 goto wait_child;
308
309         while (1) {
310                 rd = read(pipeerr[0], output + output_size,
311                           OUTPUT_BUFSIZE - output_size);
312                 if (rd < 0) {
313                         output_size = sizeof(ERRSTR_MDS_PROCESS);
314                         memcpy(output, ERRSTR_MDS_PROCESS, dc_args.reslen);
315                         break;
316                 }
317                 if (rd == 0)
318                         break;
319                 output_size += rd;
320                 if (output_size >= OUTPUT_BUFSIZE)
321                         break;
322         }
323
324 wait_child:
325         wait(&childret);
326
327         dc_args.status = childret;
328         dc_args.reslen = output_size;
329
330 downcall:
331         dc_fd = open(dc_name, O_WRONLY);
332         if (dc_fd < 0) {
333                 log_msg("can't open %s: %s\n", dc_name, strerror(errno));
334         } else {
335                 int rc;
336
337                 rc = write(dc_fd, &dc_args, sizeof(dc_args));
338                 if (rc != sizeof(dc_args))
339                         log_msg("write error: ret %d\n", rc);
340
341                 close(dc_fd);
342         }
343
344         /* XXX temp for debugging */
345         log_msg("finished upcall\n");
346         return 0;
347 }