exit less
[openafs.git] / src / sys / rmtsysc.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * This module (residing in lib/afs/librmtsys.a) implements the client side of
12  * the rpc version (via rx) of non-standard system calls. Currently only rpc
13  * calls of setpag, and pioctl are supported.
14  */
15 #include <afsconfig.h>
16 #include <afs/param.h>
17
18
19 #include <errno.h>
20 #include <limits.h>
21 #include <sys/types.h>
22 #include <afs/vice.h>
23 #ifdef AFS_NT40_ENV
24 #include <winsock2.h>
25 #else
26 #include <netdb.h>
27 #include <netinet/in.h>
28 #include <sys/file.h>
29 #endif
30 #include <sys/stat.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>    
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 #ifdef HAVE_GRP_H
38 #include <grp.h>
39 #endif
40 #include <rx/xdr.h>
41 #include "rmtsys.h"
42 #include "sys_prototypes.h"
43
44
45 #define NOPAG       0xffffffff  /* Also defined in afs/afs.h */
46 static afs_int32 hostAddr = 0;
47 static int hostAddrLookup = 0;
48 char *afs_server = 0, server_name[128];
49 static afs_int32 SetClientCreds(struct clientcred *creds, afs_uint32 * groups);
50 int afs_get_pag_from_groups(afs_uint32 g0, afs_uint32 g1);
51 void afs_get_groups_from_pag(afs_uint32 pag, afs_uint32 * g0p, afs_uint32 * g1p);
52
53 /* Picks up the name of the remote afs client host where the rmtsys 
54  * daemon resides. Since the clients may be diskless and/or readonly
55  * ones we felt it's better to rely on an shell environment
56  * (AFSSERVER) for the host name first. If that is not set, the
57  * $HOME/.AFSSERVER file is checked, otherwise the "/.AFSSERVER" is
58  * used.
59  */
60 afs_int32
61 GetAfsServerAddr(char *syscall)
62 {
63     register struct hostent *th;
64
65     if (hostAddrLookup) {
66         /* Take advantage of caching and assume that the remote host
67          * address won't change during a single program's invocation.
68          */
69         return hostAddr;
70     }
71     hostAddrLookup = 1;
72
73     if (!(afs_server = getenv("AFSSERVER"))) {
74         char *home_dir;
75         FILE *fp;
76         int len;
77
78         if (!(home_dir = getenv("HOME"))) {
79             /* Our last chance is the "/.AFSSERVER" file */
80             fp = fopen("/.AFSSERVER", "r");
81             if (fp == 0) {
82                 return 0;
83             }
84             fgets(server_name, 128, fp);
85             fclose(fp);
86         } else {
87             char pathname[256];
88
89             sprintf(pathname, "%s/%s", home_dir, ".AFSSERVER");
90             fp = fopen(pathname, "r");
91             if (fp == 0) {
92                 /* Our last chance is the "/.AFSSERVER" file */
93                 fp = fopen("/.AFSSERVER", "r");
94                 if (fp == 0) {
95                     return 0;
96                 }
97             }
98             fgets(server_name, 128, fp);
99             fclose(fp);
100         }
101         len = strlen(server_name);
102         if (len == 0) {
103             return 0;
104         }
105         if (server_name[len - 1] == '\n') {
106             server_name[len - 1] = 0;
107         }
108         afs_server = server_name;
109     }
110     th = gethostbyname(afs_server);
111     if (!th) {
112         printf("host %s not found; %s call aborted\n", afs_server, syscall);
113         return 0;
114     }
115     memcpy(&hostAddr, th->h_addr, sizeof(hostAddr));
116     return hostAddr;
117 }
118
119
120 /* Does the actual RX connection to the afs server */
121 struct rx_connection *
122 rx_connection(afs_int32 * errorcode, char *syscall)
123 {
124     struct rx_connection *conn;
125     struct rx_securityClass *null_securityObject;
126     afs_int32 host;
127
128     if (!(host = GetAfsServerAddr(syscall))) {
129         *errorcode = -1;
130         return (struct rx_connection *)0;
131     }
132     *errorcode = rx_Init(0);
133     if (*errorcode) {
134         printf("Rx initialize failed \n");
135         return (struct rx_connection *)0;
136     }
137     null_securityObject = rxnull_NewClientSecurityObject();
138     conn =
139         rx_NewConnection(host, htons(AFSCONF_RMTSYSPORT), RMTSYS_SERVICEID,
140                          null_securityObject, 0);
141     if (!conn) {
142         printf("Unable to make a new connection\n");
143         *errorcode = -1;
144         return (struct rx_connection *)0;
145     }
146     return conn;
147 }
148
149
150 /* WARNING: The calling program (i.e. klog) MUST be suid-root since we need to
151  * do a setgroups(2) call with the new pag.... */
152 #ifdef AFS_DUX40_ENV
153 #pragma weak setpag = afs_setpag
154 int
155 afs_setpag(void)
156 #else
157 int
158 setpag(void)
159 #endif
160 {
161     struct rx_connection *conn;
162     clientcred creds;
163     afs_int32 errorcode, errornumber, newpag, ngroups, j;
164     afs_uint32 groups[NGROUPS_MAX];
165     
166     if (!(conn = rx_connection(&errorcode, "setpag"))) {
167         /* Remote call can't be performed for some reason.
168          * Try the local 'setpag' system call ... */
169         errorcode = lsetpag();
170         return errorcode;
171     }
172     ngroups = SetClientCreds(&creds, groups);
173     errorcode = RMTSYS_SetPag(conn, &creds, &newpag, &errornumber);
174     if (errornumber) {
175         errno = errornumber;
176         errorcode = -1;
177         printf("Warning: Remote setpag to %s has failed (err=%d)...\n",
178                afs_server, errno);
179     }
180     if (errorcode) {
181         return errorcode;
182     }
183     if (afs_get_pag_from_groups(groups[0], groups[1]) == NOPAG) {
184         /* we will have to shift grouplist to make room for pag */
185         if (ngroups + 2 > NGROUPS_MAX) {
186             /* this is what the real setpag returns */
187             errno = E2BIG;
188             return -1;
189         }
190         for (j = ngroups - 1; j >= 0; j--) {
191             groups[j + 2] = groups[j];
192         }
193         ngroups += 2;
194     }
195     afs_get_groups_from_pag(newpag, &groups[0], &groups[1]);
196     if (setgroups(ngroups, groups) == -1) {
197         return -1;
198     }
199 #ifdef  AFS_HPUX_ENV
200     errorcode = setuid(getuid());
201 #else
202     errorcode = setreuid(-1, getuid());
203 #endif /* AFS_HPUX_ENV */
204     return errorcode;
205 }
206
207
208 /* Remote pioctl(2) client routine */
209 #ifdef AFS_DUX40_ENV
210 #pragma weak pioctl = afs_pioctl
211 int
212 afs_pioctl(char *path, afs_int32 cmd, struct ViceIoctl *data,
213            afs_int32 follow)
214 #else
215 int
216 pioctl(char *path, afs_int32 cmd, struct ViceIoctl *data, afs_int32 follow)
217 #endif
218 {
219     struct rx_connection *conn;
220     clientcred creds;
221     afs_int32 errorcode, errornumber, ins = data->in_size;
222     afs_uint32 groups[NGROUPS_MAX];
223     rmtbulk InData, OutData;
224     char pathname[256], *pathp = pathname, *inbuffer;
225     if (!(conn = rx_connection(&errorcode, "pioctl"))) {
226         /* Remote call can't be performed for some reason.
227          * Try the local 'pioctl' system call ... */
228         errorcode = lpioctl(path, cmd, data, follow);
229         return errorcode;
230     }
231     (void)SetClientCreds(&creds, groups);
232 #ifdef  AFS_OSF_ENV
233     if (!ins)
234         ins = 1;
235 #endif
236     if (!(inbuffer = (char *)malloc(ins)))
237         return (-1);            /* helpless here */
238     if (data->in_size)
239         memcpy(inbuffer, data->in, data->in_size);
240     InData.rmtbulk_len = data->in_size;
241     InData.rmtbulk_val = inbuffer;
242     inparam_conversion(cmd, InData.rmtbulk_val, 0);
243
244     OutData.rmtbulk_len = MAXBUFFERLEN * sizeof(*OutData.rmtbulk_val);
245     OutData.rmtbulk_val = malloc(OutData.rmtbulk_len); 
246     if (!OutData.rmtbulk_val) {
247         free(inbuffer);
248         return -1;
249     }
250
251     /* We always need to pass absolute pathnames to the remote pioctl since we
252      * lose the current directory value when doing an rpc call. Below we
253      * prepend the current absolute path directory, if the name is relative */
254     if (path) {
255         if (*path != '/') {
256             /* assuming relative path name */
257             if (getcwd(pathname, 256) == NULL) {
258                 free(inbuffer);
259                 printf("getwd failed\n");
260                 return -1;
261             }
262             strcpy(pathname + strlen(pathname), "/");
263             strcat(pathname, path);
264         } else {
265             strcpy(pathname, path);
266         }
267     } else {
268         /* Special meaning for a "NULL" pathname since xdr_string hates nil
269          * pointers, at least on non-RTS; of course the proper solution would
270          * be to change the interface declartion. */
271         strcpy(pathname, NIL_PATHP);
272     }
273     errorcode =
274         RMTSYS_Pioctl(conn, &creds, pathp, cmd, follow, &InData, &OutData,
275                       &errornumber);
276     if (errornumber) {
277         errno = errornumber;
278         errorcode = -1;         /* Necessary since errorcode is 0 on
279                                  * standard remote pioctl errors */
280         if (errno != EDOM && errno != EACCES)
281             printf("Warning: Remote pioctl to %s has failed (err=%d)...\n",
282                    afs_server, errno);
283     }
284     if (!errorcode) {
285         /* Do the conversions back to the host order; store the results back
286          * on the same buffer */
287         if (data->out_size < OutData.rmtbulk_len) {
288             errno = EINVAL;
289             errorcode = -1;
290         } else {
291             memcpy(data->out, OutData.rmtbulk_val, data->out_size);
292             outparam_conversion(cmd, data->out, 1);
293         }
294     }
295     free(OutData.rmtbulk_val);
296     free(inbuffer);
297     return errorcode;
298 }
299
300
301 int
302 afs_get_pag_from_groups(afs_uint32 g0, afs_uint32 g1)
303 {
304     afs_uint32 h, l, result;
305
306     g0 -= 0x3f00;
307     g1 -= 0x3f00;
308     if (g0 < 0xc000 && g1 < 0xc000) {
309         l = ((g0 & 0x3fff) << 14) | (g1 & 0x3fff);
310         h = (g0 >> 14);
311         h = (g1 >> 14) + h + h + h;
312         result = ((h << 28) | l);
313         /* Additional testing */
314         if (((result >> 24) & 0xff) == 'A')
315             return result;
316         else
317             return NOPAG;
318     }
319     return NOPAG;
320 }
321
322 void
323 afs_get_groups_from_pag(afs_uint32 pag, afs_uint32 * g0p, afs_uint32 * g1p)
324 {
325     unsigned short g0, g1;
326
327     pag &= 0x7fffffff;
328     g0 = 0x3fff & (pag >> 14);
329     g1 = 0x3fff & pag;
330     g0 |= ((pag >> 28) / 3) << 14;
331     g1 |= ((pag >> 28) % 3) << 14;
332     *g0p = g0 + 0x3f00;
333     *g1p = g1 + 0x3f00;
334 }
335
336
337 static afs_int32
338 SetClientCreds(struct clientcred *creds, afs_uint32 * groups)
339 {
340     afs_int32 ngroups;
341
342     creds->uid = getuid();
343     groups[0] = groups[1] = 0;
344     ngroups = getgroups(NGROUPS_MAX, groups);
345     creds->group0 = groups[0];
346     creds->group1 = groups[1];
347     return ngroups;
348 }