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