Import Upstream version 1.8.5
[hcoop/debian/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 = 0;
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 } else {
69 char *pathname;
70
71 len = asprintf(&pathname, "%s/%s", home_dir, ".AFSSERVER");
72 if (len < 0 || pathname == NULL)
73 return 0;
74 fp = fopen(pathname, "r");
75 free(pathname);
76
77 if (fp == 0) {
78 /* Our last chance is the "/.AFSSERVER" file */
79 fp = fopen("/.AFSSERVER", "r");
80 if (fp == 0) {
81 return 0;
82 }
83 }
84 }
85 if (fgets(server_name, 128, fp) != NULL)
86 len = strlen(server_name);
87 fclose(fp);
88 if (len == 0) {
89 return 0;
90 }
91 if (server_name[len - 1] == '\n') {
92 server_name[len - 1] = 0;
93 }
94 afs_server = server_name;
95 }
96 th = gethostbyname(afs_server);
97 if (!th) {
98 printf("host %s not found; %s call aborted\n", afs_server, syscall);
99 return 0;
100 }
101 memcpy(&hostAddr, th->h_addr, sizeof(hostAddr));
102 return hostAddr;
103 }
104
105
106 /* Does the actual RX connection to the afs server */
107 struct rx_connection *
108 rx_connection(afs_int32 * errorcode, char *syscall)
109 {
110 struct rx_connection *conn;
111 struct rx_securityClass *null_securityObject;
112 afs_int32 host;
113
114 if (!(host = GetAfsServerAddr(syscall))) {
115 *errorcode = -1;
116 return (struct rx_connection *)0;
117 }
118 *errorcode = rx_Init(0);
119 if (*errorcode) {
120 printf("Rx initialize failed \n");
121 return (struct rx_connection *)0;
122 }
123 null_securityObject = rxnull_NewClientSecurityObject();
124 conn =
125 rx_NewConnection(host, htons(AFSCONF_RMTSYSPORT), RMTSYS_SERVICEID,
126 null_securityObject, RX_SECIDX_NULL);
127 if (!conn) {
128 printf("Unable to make a new connection\n");
129 *errorcode = -1;
130 return (struct rx_connection *)0;
131 }
132 return conn;
133 }
134
135
136 /* WARNING: The calling program (i.e. klog) MUST be suid-root since we need to
137 * do a setgroups(2) call with the new pag.... */
138 #ifdef AFS_DUX40_ENV
139 #pragma weak setpag = afs_setpag
140 int
141 afs_setpag(void)
142 #else
143 int
144 setpag(void)
145 #endif
146 {
147 struct rx_connection *conn;
148 clientcred creds;
149 afs_int32 errorcode, errornumber, newpag, ngroups, j;
150 afs_uint32 groups[NGROUPS_MAX];
151
152 if (!(conn = rx_connection(&errorcode, "setpag"))) {
153 /* Remote call can't be performed for some reason.
154 * Try the local 'setpag' system call ... */
155 errorcode = lsetpag();
156 return errorcode;
157 }
158 ngroups = SetClientCreds(&creds, groups);
159 errorcode = RMTSYS_SetPag(conn, &creds, &newpag, &errornumber);
160 if (errornumber) {
161 errno = errornumber;
162 errorcode = -1;
163 printf("Warning: Remote setpag to %s has failed (err=%d)...\n",
164 afs_server, errno);
165 }
166 if (errorcode) {
167 return errorcode;
168 }
169 if (afs_get_pag_from_groups(groups[0], groups[1]) == NOPAG) {
170 /* we will have to shift grouplist to make room for pag */
171 if (ngroups + 2 > NGROUPS_MAX) {
172 /* this is what the real setpag returns */
173 errno = E2BIG;
174 return -1;
175 }
176 for (j = ngroups - 1; j >= 0; j--) {
177 groups[j + 2] = groups[j];
178 }
179 ngroups += 2;
180 }
181 afs_get_groups_from_pag(newpag, &groups[0], &groups[1]);
182 if (setgroups(ngroups, groups) == -1) {
183 return -1;
184 }
185 #ifdef AFS_HPUX_ENV
186 errorcode = setuid(getuid());
187 #else
188 errorcode = setreuid(-1, getuid());
189 #endif /* AFS_HPUX_ENV */
190 return errorcode;
191 }
192
193
194 /* Remote pioctl(2) client routine */
195 #ifdef AFS_DUX40_ENV
196 #pragma weak pioctl = afs_pioctl
197 int
198 afs_pioctl(char *path, afs_int32 cmd, struct ViceIoctl *data,
199 afs_int32 follow)
200 #else
201 int
202 pioctl(char *path, afs_int32 cmd, struct ViceIoctl *data, afs_int32 follow)
203 #endif
204 {
205 struct rx_connection *conn;
206 clientcred creds;
207 afs_int32 errorcode, errornumber, ins = data->in_size;
208 afs_uint32 groups[NGROUPS_MAX];
209 rmtbulk InData, OutData;
210 char pathname[256], *pathp = pathname, *inbuffer;
211 if (!(conn = rx_connection(&errorcode, "pioctl"))) {
212 /* Remote call can't be performed for some reason.
213 * Try the local 'pioctl' system call ... */
214 errorcode = lpioctl(path, cmd, data, follow);
215 return errorcode;
216 }
217 (void)SetClientCreds(&creds, groups);
218 #ifdef AFS_OSF_ENV
219 if (!ins)
220 ins = 1;
221 #endif
222 if (!(inbuffer = malloc(ins)))
223 return (-1); /* helpless here */
224 if (data->in_size)
225 memcpy(inbuffer, data->in, data->in_size);
226 InData.rmtbulk_len = data->in_size;
227 InData.rmtbulk_val = inbuffer;
228 inparam_conversion(cmd, InData.rmtbulk_val, 0);
229
230 OutData.rmtbulk_len = MAXBUFFERLEN * sizeof(*OutData.rmtbulk_val);
231 OutData.rmtbulk_val = malloc(OutData.rmtbulk_len);
232 if (!OutData.rmtbulk_val) {
233 free(inbuffer);
234 return -1;
235 }
236
237 /* We always need to pass absolute pathnames to the remote pioctl since we
238 * lose the current directory value when doing an rpc call. Below we
239 * prepend the current absolute path directory, if the name is relative */
240 if (path) {
241 if (*path != '/') {
242 /* assuming relative path name */
243 if (getcwd(pathname, 256) == NULL) {
244 free(inbuffer);
245 printf("getwd failed\n");
246 return -1;
247 }
248 strcpy(pathname + strlen(pathname), "/");
249 strcat(pathname, path);
250 } else {
251 strcpy(pathname, path);
252 }
253 } else {
254 /* Special meaning for a "NULL" pathname since xdr_string hates nil
255 * pointers, at least on non-RTS; of course the proper solution would
256 * be to change the interface declartion. */
257 strcpy(pathname, NIL_PATHP);
258 }
259 errorcode =
260 RMTSYS_Pioctl(conn, &creds, pathp, cmd, follow, &InData, &OutData,
261 &errornumber);
262 if (errornumber) {
263 errno = errornumber;
264 errorcode = -1; /* Necessary since errorcode is 0 on
265 * standard remote pioctl errors */
266 if (errno != EDOM && errno != EACCES)
267 printf("Warning: Remote pioctl to %s has failed (err=%d)...\n",
268 afs_server, errno);
269 }
270 if (!errorcode) {
271 /* Do the conversions back to the host order; store the results back
272 * on the same buffer */
273 if (data->out_size < OutData.rmtbulk_len) {
274 errno = EINVAL;
275 errorcode = -1;
276 } else {
277 memcpy(data->out, OutData.rmtbulk_val, data->out_size);
278 outparam_conversion(cmd, data->out, 1);
279 }
280 }
281 free(OutData.rmtbulk_val);
282 free(inbuffer);
283 return errorcode;
284 }
285
286
287 int
288 afs_get_pag_from_groups(afs_uint32 g0, afs_uint32 g1)
289 {
290 afs_uint32 h, l, result;
291
292 g0 -= 0x3f00;
293 g1 -= 0x3f00;
294 if (g0 < 0xc000 && g1 < 0xc000) {
295 l = ((g0 & 0x3fff) << 14) | (g1 & 0x3fff);
296 h = (g0 >> 14);
297 h = (g1 >> 14) + h + h + h;
298 result = ((h << 28) | l);
299 /* Additional testing */
300 if (((result >> 24) & 0xff) == 'A')
301 return result;
302 else
303 return NOPAG;
304 }
305 return NOPAG;
306 }
307
308 void
309 afs_get_groups_from_pag(afs_uint32 pag, afs_uint32 * g0p, afs_uint32 * g1p)
310 {
311 unsigned short g0, g1;
312
313 pag &= 0x7fffffff;
314 g0 = 0x3fff & (pag >> 14);
315 g1 = 0x3fff & pag;
316 g0 |= ((pag >> 28) / 3) << 14;
317 g1 |= ((pag >> 28) % 3) << 14;
318 *g0p = g0 + 0x3f00;
319 *g1p = g1 + 0x3f00;
320 }
321
322
323 static afs_int32
324 SetClientCreds(struct clientcred *creds, afs_uint32 * groups)
325 {
326 afs_int32 ngroups;
327
328 creds->uid = getuid();
329 groups[0] = groups[1] = 0;
330 ngroups = getgroups(NGROUPS_MAX, groups);
331 creds->group0 = groups[0];
332 creds->group1 = groups[1];
333 return ngroups;
334 }