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