1 /*	$NetBSD: afssys.c,v 1.1.1.2 2014/04/24 12:45:49 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 - 2000, 2002, 2004, 2005 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "kafs_locl.h"
37 
38 struct procdata {
39     unsigned long param4;
40     unsigned long param3;
41     unsigned long param2;
42     unsigned long param1;
43     unsigned long syscall;
44 };
45 #define VIOC_SYSCALL_PROC _IOW('C', 1, void *)
46 
47 struct devdata {
48     unsigned long syscall;
49     unsigned long param1;
50     unsigned long param2;
51     unsigned long param3;
52     unsigned long param4;
53     unsigned long param5;
54     unsigned long param6;
55     unsigned long retval;
56 };
57 #ifdef _IOWR
58 #define VIOC_SYSCALL_DEV _IOWR('C', 2, struct devdata)
59 #define VIOC_SYSCALL_DEV_OPENAFS _IOWR('C', 1, struct devdata)
60 #endif
61 
62 
63 int _kafs_debug; /* this should be done in a better way */
64 
65 #define UNKNOWN_ENTRY_POINT	(-1)
66 #define NO_ENTRY_POINT		0
67 #define SINGLE_ENTRY_POINT	1
68 #define MULTIPLE_ENTRY_POINT	2
69 #define SINGLE_ENTRY_POINT2	3
70 #define SINGLE_ENTRY_POINT3	4
71 #define LINUX_PROC_POINT	5
72 #define AIX_ENTRY_POINTS	6
73 #define MACOS_DEV_POINT		7
74 
75 static int afs_entry_point = UNKNOWN_ENTRY_POINT;
76 static int afs_syscalls[2];
77 static char *afs_ioctlpath;
78 static unsigned long afs_ioctlnum;
79 
80 /* Magic to get AIX syscalls to work */
81 #ifdef _AIX
82 
83 static int (*Pioctl)(char*, int, struct ViceIoctl*, int);
84 static int (*Setpag)(void);
85 
86 #include "dlfcn.h"
87 
88 /*
89  *
90  */
91 
92 static int
try_aix(void)93 try_aix(void)
94 {
95 #ifdef STATIC_AFS_SYSCALLS
96     Pioctl = aix_pioctl;
97     Setpag = aix_setpag;
98 #else
99     void *ptr;
100     char path[MaxPathLen], *p;
101     /*
102      * If we are root or running setuid don't trust AFSLIBPATH!
103      */
104     if (getuid() != 0 && !issuid() && (p = getenv("AFSLIBPATH")) != NULL)
105 	strlcpy(path, p, sizeof(path));
106     else
107 	snprintf(path, sizeof(path), "%s/afslib.so", LIBDIR);
108 
109     ptr = dlopen(path, RTLD_NOW);
110     if(ptr == NULL) {
111 	if(_kafs_debug) {
112 	    if(errno == ENOEXEC && (p = dlerror()) != NULL)
113 		fprintf(stderr, "dlopen(%s): %s\n", path, p);
114 	    else if (errno != ENOENT)
115 		fprintf(stderr, "dlopen(%s): %s\n", path, strerror(errno));
116 	}
117 	return 1;
118     }
119     Setpag = (int (*)(void))dlsym(ptr, "aix_setpag");
120     Pioctl = (int (*)(char*, int,
121 		      struct ViceIoctl*, int))dlsym(ptr, "aix_pioctl");
122 #endif
123     afs_entry_point = AIX_ENTRY_POINTS;
124     return 0;
125 }
126 #endif /* _AIX */
127 
128 /*
129  * This probably only works under Solaris and could get confused if
130  * there's a /etc/name_to_sysnum file.
131  */
132 
133 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
134 
135 #define _PATH_ETC_NAME_TO_SYSNUM "/etc/name_to_sysnum"
136 
137 static int
map_syscall_name_to_number(const char * str,int * res)138 map_syscall_name_to_number (const char *str, int *res)
139 {
140     FILE *f;
141     char buf[256];
142     size_t str_len = strlen (str);
143 
144     f = fopen (_PATH_ETC_NAME_TO_SYSNUM, "r");
145     if (f == NULL)
146 	return -1;
147     while (fgets (buf, sizeof(buf), f) != NULL) {
148 	if (buf[0] == '#')
149 	    continue;
150 
151 	if (strncmp (str, buf, str_len) == 0) {
152 	    char *begptr = buf + str_len;
153 	    char *endptr;
154 	    long val = strtol (begptr, &endptr, 0);
155 
156 	    if (val != 0 && endptr != begptr) {
157 		fclose (f);
158 		*res = val;
159 		return 0;
160 	    }
161 	}
162     }
163     fclose (f);
164     return -1;
165 }
166 #endif
167 
168 static int
try_ioctlpath(const char * path,unsigned long ioctlnum,int entrypoint)169 try_ioctlpath(const char *path, unsigned long ioctlnum, int entrypoint)
170 {
171     int fd, ret, saved_errno;
172 
173     fd = open(path, O_RDWR);
174     if (fd < 0)
175 	return 1;
176     switch (entrypoint) {
177     case LINUX_PROC_POINT: {
178 	struct procdata data = { 0, 0, 0, 0, AFSCALL_PIOCTL };
179 	data.param2 = (unsigned long)VIOCGETTOK;
180 	ret = ioctl(fd, ioctlnum, &data);
181 	break;
182     }
183     case MACOS_DEV_POINT: {
184 	struct devdata data = { AFSCALL_PIOCTL, 0, 0, 0, 0, 0, 0, 0 };
185 	data.param2 = (unsigned long)VIOCGETTOK;
186 	ret = ioctl(fd, ioctlnum, &data);
187 	break;
188     }
189     default:
190 	abort();
191     }
192     saved_errno = errno;
193     close(fd);
194     /*
195      * Be quite liberal in what error are ok, the first is the one
196      * that should trigger given that params is NULL.
197      */
198     if (ret &&
199 	(saved_errno != EFAULT &&
200 	 saved_errno != EDOM &&
201 	 saved_errno != ENOTCONN))
202 	return 1;
203     afs_ioctlnum = ioctlnum;
204     afs_ioctlpath = strdup(path);
205     if (afs_ioctlpath == NULL)
206 	return 1;
207     afs_entry_point = entrypoint;
208     return 0;
209 }
210 
211 static int
do_ioctl(void * data)212 do_ioctl(void *data)
213 {
214     int fd, ret, saved_errno;
215     fd = open(afs_ioctlpath, O_RDWR);
216     if (fd < 0) {
217 	errno = EINVAL;
218 	return -1;
219     }
220     ret = ioctl(fd, afs_ioctlnum, data);
221     saved_errno = errno;
222     close(fd);
223     errno = saved_errno;
224     return ret;
225 }
226 
227 int
k_pioctl(char * a_path,int o_opcode,struct ViceIoctl * a_paramsP,int a_followSymlinks)228 k_pioctl(char *a_path,
229 	 int o_opcode,
230 	 struct ViceIoctl *a_paramsP,
231 	 int a_followSymlinks)
232 {
233 #ifndef NO_AFS
234     switch(afs_entry_point){
235 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
236     case SINGLE_ENTRY_POINT:
237     case SINGLE_ENTRY_POINT2:
238     case SINGLE_ENTRY_POINT3:
239 	return syscall(afs_syscalls[0], AFSCALL_PIOCTL,
240 		       a_path, o_opcode, a_paramsP, a_followSymlinks);
241 #endif
242 #if defined(AFS_PIOCTL)
243     case MULTIPLE_ENTRY_POINT:
244 	return syscall(afs_syscalls[0],
245 		       a_path, o_opcode, a_paramsP, a_followSymlinks);
246 #endif
247     case LINUX_PROC_POINT: {
248 	struct procdata data = { 0, 0, 0, 0, AFSCALL_PIOCTL };
249 	data.param1 = (unsigned long)a_path;
250 	data.param2 = (unsigned long)o_opcode;
251 	data.param3 = (unsigned long)a_paramsP;
252 	data.param4 = (unsigned long)a_followSymlinks;
253 	return do_ioctl(&data);
254     }
255     case MACOS_DEV_POINT: {
256 	struct devdata data = { AFSCALL_PIOCTL, 0, 0, 0, 0, 0, 0, 0 };
257 	int ret;
258 
259 	data.param1 = (unsigned long)a_path;
260 	data.param2 = (unsigned long)o_opcode;
261 	data.param3 = (unsigned long)a_paramsP;
262 	data.param4 = (unsigned long)a_followSymlinks;
263 
264 	ret = do_ioctl(&data);
265 	if (ret)
266 	    return ret;
267 
268 	return data.retval;
269     }
270 #ifdef _AIX
271     case AIX_ENTRY_POINTS:
272 	return Pioctl(a_path, o_opcode, a_paramsP, a_followSymlinks);
273 #endif
274     }
275     errno = ENOSYS;
276 #ifdef SIGSYS
277     kill(getpid(), SIGSYS);	/* You lose! */
278 #endif
279 #endif /* NO_AFS */
280     return -1;
281 }
282 
283 int
k_afs_cell_of_file(const char * path,char * cell,int len)284 k_afs_cell_of_file(const char *path, char *cell, int len)
285 {
286     struct ViceIoctl parms;
287     parms.in = NULL;
288     parms.in_size = 0;
289     parms.out = cell;
290     parms.out_size = len;
291     return k_pioctl(rk_UNCONST(path), VIOC_FILE_CELL_NAME, &parms, 1);
292 }
293 
294 int
k_unlog(void)295 k_unlog(void)
296 {
297     struct ViceIoctl parms;
298     memset(&parms, 0, sizeof(parms));
299     return k_pioctl(0, VIOCUNLOG, &parms, 0);
300 }
301 
302 int
k_setpag(void)303 k_setpag(void)
304 {
305 #ifndef NO_AFS
306     switch(afs_entry_point){
307 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
308     case SINGLE_ENTRY_POINT:
309     case SINGLE_ENTRY_POINT2:
310     case SINGLE_ENTRY_POINT3:
311 	return syscall(afs_syscalls[0], AFSCALL_SETPAG);
312 #endif
313 #if defined(AFS_PIOCTL)
314     case MULTIPLE_ENTRY_POINT:
315 	return syscall(afs_syscalls[1]);
316 #endif
317     case LINUX_PROC_POINT: {
318 	struct procdata data = { 0, 0, 0, 0, AFSCALL_SETPAG };
319 	return do_ioctl(&data);
320     }
321     case MACOS_DEV_POINT: {
322 	struct devdata data = { AFSCALL_SETPAG, 0, 0, 0, 0, 0, 0, 0 };
323 	int ret = do_ioctl(&data);
324 	if (ret)
325 	    return ret;
326 	return data.retval;
327      }
328 #ifdef _AIX
329     case AIX_ENTRY_POINTS:
330 	return Setpag();
331 #endif
332     }
333 
334     errno = ENOSYS;
335 #ifdef SIGSYS
336     kill(getpid(), SIGSYS);	/* You lose! */
337 #endif
338 #endif /* NO_AFS */
339     return -1;
340 }
341 
342 static jmp_buf catch_SIGSYS;
343 
344 #ifdef SIGSYS
345 
346 static RETSIGTYPE
SIGSYS_handler(int sig)347 SIGSYS_handler(int sig)
348 {
349     errno = 0;
350     signal(SIGSYS, SIGSYS_handler); /* Need to reinstall handler on SYSV */
351     longjmp(catch_SIGSYS, 1);
352 }
353 
354 #endif
355 
356 /*
357  * Try to see if `syscall' is a pioctl.  Return 0 iff succesful.
358  */
359 
360 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
361 static int
try_one(int syscall_num)362 try_one (int syscall_num)
363 {
364     struct ViceIoctl parms;
365     memset(&parms, 0, sizeof(parms));
366 
367     if (setjmp(catch_SIGSYS) == 0) {
368 	syscall(syscall_num, AFSCALL_PIOCTL,
369 		0, VIOCSETTOK, &parms, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
370 	if (errno == EINVAL) {
371 	    afs_entry_point = SINGLE_ENTRY_POINT;
372 	    afs_syscalls[0] = syscall_num;
373 	    return 0;
374 	}
375     }
376     return 1;
377 }
378 #endif
379 
380 /*
381  * Try to see if `syscall_pioctl' is a pioctl syscall.  Return 0 iff
382  * succesful.
383  *
384  */
385 
386 #ifdef AFS_PIOCTL
387 static int
try_two(int syscall_pioctl,int syscall_setpag)388 try_two (int syscall_pioctl, int syscall_setpag)
389 {
390     struct ViceIoctl parms;
391     memset(&parms, 0, sizeof(parms));
392 
393     if (setjmp(catch_SIGSYS) == 0) {
394 	syscall(syscall_pioctl,
395 		0, VIOCSETTOK, &parms, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
396 	if (errno == EINVAL) {
397 	    afs_entry_point = MULTIPLE_ENTRY_POINT;
398 	    afs_syscalls[0] = syscall_pioctl;
399 	    afs_syscalls[1] = syscall_setpag;
400 	    return 0;
401 	}
402     }
403     return 1;
404 }
405 #endif
406 
407 int
k_hasafs(void)408 k_hasafs(void)
409 {
410 #if !defined(NO_AFS) && defined(SIGSYS)
411     RETSIGTYPE (*saved_func)(int);
412 #endif
413     int saved_errno, ret;
414     char *env = NULL;
415 
416     if (!issuid())
417 	env = getenv ("AFS_SYSCALL");
418 
419     /*
420      * Already checked presence of AFS syscalls?
421      */
422     if (afs_entry_point != UNKNOWN_ENTRY_POINT)
423 	return afs_entry_point != NO_ENTRY_POINT;
424 
425     /*
426      * Probe kernel for AFS specific syscalls,
427      * they (currently) come in two flavors.
428      * If the syscall is absent we recive a SIGSYS.
429      */
430     afs_entry_point = NO_ENTRY_POINT;
431 
432     saved_errno = errno;
433 #ifndef NO_AFS
434 #ifdef SIGSYS
435     saved_func = signal(SIGSYS, SIGSYS_handler);
436 #endif
437     if (env && strstr(env, "..") == NULL) {
438 
439 	if (strncmp("/proc/", env, 6) == 0) {
440 	    if (try_ioctlpath(env, VIOC_SYSCALL_PROC, LINUX_PROC_POINT) == 0)
441 		goto done;
442 	}
443 	if (strncmp("/dev/", env, 5) == 0) {
444 #ifdef VIOC_SYSCALL_DEV
445 	    if (try_ioctlpath(env, VIOC_SYSCALL_DEV, MACOS_DEV_POINT) == 0)
446 		goto done;
447 #endif
448 #ifdef VIOC_SYSCALL_DEV_OPENAFS
449 	    if (try_ioctlpath(env,VIOC_SYSCALL_DEV_OPENAFS,MACOS_DEV_POINT) ==0)
450 		goto done;
451 #endif
452 	}
453     }
454 
455     ret = try_ioctlpath("/proc/fs/openafs/afs_ioctl",
456 			VIOC_SYSCALL_PROC, LINUX_PROC_POINT);
457     if (ret == 0)
458 	goto done;
459     ret = try_ioctlpath("/proc/fs/nnpfs/afs_ioctl",
460 			VIOC_SYSCALL_PROC, LINUX_PROC_POINT);
461     if (ret == 0)
462 	goto done;
463 
464 #ifdef VIOC_SYSCALL_DEV_OPENAFS
465     ret = try_ioctlpath("/dev/openafs_ioctl",
466 			VIOC_SYSCALL_DEV_OPENAFS, MACOS_DEV_POINT);
467     if (ret == 0)
468 	goto done;
469 #endif
470 #ifdef VIOC_SYSCALL_DEV
471     ret = try_ioctlpath("/dev/nnpfs_ioctl", VIOC_SYSCALL_DEV, MACOS_DEV_POINT);
472     if (ret == 0)
473 	goto done;
474 #endif
475 
476 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
477     {
478 	int tmp;
479 
480 	if (env != NULL) {
481 	    if (sscanf (env, "%d", &tmp) == 1) {
482 		if (try_one (tmp) == 0)
483 		    goto done;
484 	    } else {
485 		char *end = NULL;
486 		char *p;
487 		char *s = strdup (env);
488 
489 		if (s != NULL) {
490 		    for (p = strtok_r (s, ",", &end);
491 			 p != NULL;
492 			 p = strtok_r (NULL, ",", &end)) {
493 			if (map_syscall_name_to_number (p, &tmp) == 0)
494 			    if (try_one (tmp) == 0) {
495 				free (s);
496 				goto done;
497 			    }
498 		    }
499 		    free (s);
500 		}
501 	    }
502 	}
503     }
504 #endif /* AFS_SYSCALL || AFS_SYSCALL2 || AFS_SYSCALL3 */
505 
506 #ifdef AFS_SYSCALL
507     if (try_one (AFS_SYSCALL) == 0)
508 	goto done;
509 #endif /* AFS_SYSCALL */
510 
511 #ifdef AFS_PIOCTL
512     {
513 	int tmp[2];
514 
515 	if (env != NULL && sscanf (env, "%d%d", &tmp[0], &tmp[1]) == 2)
516 	    if (try_two (tmp[0], tmp[1]) == 2)
517 		goto done;
518     }
519 #endif /* AFS_PIOCTL */
520 
521 #ifdef AFS_PIOCTL
522     if (try_two (AFS_PIOCTL, AFS_SETPAG) == 0)
523 	goto done;
524 #endif /* AFS_PIOCTL */
525 
526 #ifdef AFS_SYSCALL2
527     if (try_one (AFS_SYSCALL2) == 0)
528 	goto done;
529 #endif /* AFS_SYSCALL2 */
530 
531 #ifdef AFS_SYSCALL3
532     if (try_one (AFS_SYSCALL3) == 0)
533 	goto done;
534 #endif /* AFS_SYSCALL3 */
535 
536 #ifdef _AIX
537 #if 0
538     if (env != NULL) {
539 	char *pos = NULL;
540 	char *pioctl_name;
541 	char *setpag_name;
542 
543 	pioctl_name = strtok_r (env, ", \t", &pos);
544 	if (pioctl_name != NULL) {
545 	    setpag_name = strtok_r (NULL, ", \t", &pos);
546 	    if (setpag_name != NULL)
547 		if (try_aix (pioctl_name, setpag_name) == 0)
548 		    goto done;
549 	}
550     }
551 #endif
552 
553     if(try_aix() == 0)
554 	goto done;
555 #endif
556 
557 
558 done:
559 #ifdef SIGSYS
560     signal(SIGSYS, saved_func);
561 #endif
562 #endif /* NO_AFS */
563     errno = saved_errno;
564     return afs_entry_point != NO_ENTRY_POINT;
565 }
566 
567 int
k_hasafs_recheck(void)568 k_hasafs_recheck(void)
569 {
570     afs_entry_point = UNKNOWN_ENTRY_POINT;
571     return k_hasafs();
572 }
573