xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/kafs/afssys.c (revision d3273b5b76f5afaafe308cead5511dbb8df8c5e9)
1 /*	$NetBSD: afssys.c,v 1.2 2017/01/28 21:31:49 christos 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 #ifdef __GNU__
46 #define _IOT_procdata _IOT(_IOTS(long), 5, 0, 0, 0, 0)
47 #define VIOC_SYSCALL_PROC _IOW('C', 1, struct procdata)
48 #else
49 #define VIOC_SYSCALL_PROC _IOW('C', 1, void *)
50 #endif
51 
52 struct devdata {
53     unsigned long syscall;
54     unsigned long param1;
55     unsigned long param2;
56     unsigned long param3;
57     unsigned long param4;
58     unsigned long param5;
59     unsigned long param6;
60     unsigned long retval;
61 };
62 #ifdef __GNU__
63 #define _IOT_devdata _IOT(_IOTS(long), 8, 0, 0, 0, 0)
64 #endif
65 #ifdef _IOWR
66 #define VIOC_SYSCALL_DEV _IOWR('C', 2, struct devdata)
67 #define VIOC_SYSCALL_DEV_OPENAFS _IOWR('C', 1, struct devdata)
68 #endif
69 
70 #ifdef _IOW
71 #ifdef _ILP32
72 struct sundevdata {
73     uint32_t param6;
74     uint32_t param5;
75     uint32_t param4;
76     uint32_t param3;
77     uint32_t param2;
78     uint32_t param1;
79     uint32_t syscall;
80 };
81 #define VIOC_SUN_SYSCALL_DEV _IOW('C', 2, struct sundevdata)
82 #else
83 struct sundevdata {
84     uint64_t param6;
85     uint64_t param5;
86     uint64_t param4;
87     uint64_t param3;
88     uint64_t param2;
89     uint64_t param1;
90     uint64_t syscall;
91 };
92 #define VIOC_SUN_SYSCALL_DEV _IOW('C', 1, struct sundevdata)
93 #endif
94 #endif /* _IOW */
95 
96 
97 int _kafs_debug; /* this should be done in a better way */
98 
99 #define UNKNOWN_ENTRY_POINT	(-1)
100 #define NO_ENTRY_POINT		0
101 #define SINGLE_ENTRY_POINT	1
102 #define MULTIPLE_ENTRY_POINT	2
103 #define SINGLE_ENTRY_POINT2	3
104 #define SINGLE_ENTRY_POINT3	4
105 #define LINUX_PROC_POINT	5
106 #define AIX_ENTRY_POINTS	6
107 #define MACOS_DEV_POINT		7
108 #define SUN_PROC_POINT		8
109 
110 static int afs_entry_point = UNKNOWN_ENTRY_POINT;
111 static int afs_syscalls[2];
112 static char *afs_ioctlpath;
113 static unsigned long afs_ioctlnum;
114 
115 /* Magic to get AIX syscalls to work */
116 #ifdef _AIX
117 
118 static int (*Pioctl)(char*, int, struct ViceIoctl*, int);
119 static int (*Setpag)(void);
120 
121 #include "dlfcn.h"
122 
123 /*
124  *
125  */
126 
127 static int
try_aix(void)128 try_aix(void)
129 {
130 #ifdef STATIC_AFS_SYSCALLS
131     Pioctl = aix_pioctl;
132     Setpag = aix_setpag;
133 #else
134     void *ptr;
135     char path[MaxPathLen], *p;
136     /*
137      * If we are root or running setuid don't trust AFSLIBPATH!
138      */
139     if (getuid() != 0 && !issuid() && (p = getenv("AFSLIBPATH")) != NULL)
140 	strlcpy(path, p, sizeof(path));
141     else
142 	snprintf(path, sizeof(path), "%s/afslib.so", LIBDIR);
143 
144     ptr = dlopen(path, RTLD_NOW);
145     if(ptr == NULL) {
146 	if(_kafs_debug) {
147 	    if(errno == ENOEXEC && (p = dlerror()) != NULL)
148 		fprintf(stderr, "dlopen(%s): %s\n", path, p);
149 	    else if (errno != ENOENT)
150 		fprintf(stderr, "dlopen(%s): %s\n", path, strerror(errno));
151 	}
152 	return 1;
153     }
154     Setpag = (int (*)(void))dlsym(ptr, "aix_setpag");
155     Pioctl = (int (*)(char*, int,
156 		      struct ViceIoctl*, int))dlsym(ptr, "aix_pioctl");
157 #endif
158     afs_entry_point = AIX_ENTRY_POINTS;
159     return 0;
160 }
161 #endif /* _AIX */
162 
163 /*
164  * This probably only works under Solaris and could get confused if
165  * there's a /etc/name_to_sysnum file.
166  */
167 
168 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
169 
170 #define _PATH_ETC_NAME_TO_SYSNUM "/etc/name_to_sysnum"
171 
172 static int
map_syscall_name_to_number(const char * str,int * res)173 map_syscall_name_to_number (const char *str, int *res)
174 {
175     FILE *f;
176     char buf[256];
177     size_t str_len = strlen (str);
178 
179     f = fopen (_PATH_ETC_NAME_TO_SYSNUM, "r");
180     if (f == NULL)
181 	return -1;
182     while (fgets (buf, sizeof(buf), f) != NULL) {
183 	if (buf[0] == '#')
184 	    continue;
185 
186 	if (strncmp (str, buf, str_len) == 0) {
187 	    char *begptr = buf + str_len;
188 	    char *endptr;
189 	    long val = strtol (begptr, &endptr, 0);
190 
191 	    if (val != 0 && endptr != begptr) {
192 		fclose (f);
193 		*res = val;
194 		return 0;
195 	    }
196 	}
197     }
198     fclose (f);
199     return -1;
200 }
201 #endif
202 
203 static int
try_ioctlpath(const char * path,unsigned long ioctlnum,int entrypoint)204 try_ioctlpath(const char *path, unsigned long ioctlnum, int entrypoint)
205 {
206     int fd, ret, saved_errno;
207 
208     fd = open(path, O_RDWR);
209     if (fd < 0)
210 	return 1;
211     switch (entrypoint) {
212     case LINUX_PROC_POINT: {
213 	struct procdata data = { 0, 0, 0, 0, AFSCALL_PIOCTL };
214 	data.param2 = (unsigned long)VIOCGETTOK;
215 	ret = ioctl(fd, ioctlnum, &data);
216 	break;
217     }
218     case MACOS_DEV_POINT: {
219 	struct devdata data = { AFSCALL_PIOCTL, 0, 0, 0, 0, 0, 0, 0 };
220 	data.param2 = (unsigned long)VIOCGETTOK;
221 	ret = ioctl(fd, ioctlnum, &data);
222 	break;
223     }
224     case SUN_PROC_POINT: {
225 	struct sundevdata data = { 0, 0, 0, 0, 0, 0, AFSCALL_PIOCTL };
226 	data.param2 = (unsigned long)VIOCGETTOK;
227 	ret = ioctl(fd, ioctlnum, &data);
228 	break;
229     }
230     default:
231 	abort();
232     }
233     saved_errno = errno;
234     close(fd);
235     /*
236      * Be quite liberal in what error are ok, the first is the one
237      * that should trigger given that params is NULL.
238      */
239     if (ret &&
240 	(saved_errno != EFAULT &&
241 	 saved_errno != EDOM &&
242 	 saved_errno != ENOTCONN))
243 	return 1;
244     afs_ioctlnum = ioctlnum;
245     afs_ioctlpath = strdup(path);
246     if (afs_ioctlpath == NULL)
247 	return 1;
248     afs_entry_point = entrypoint;
249     return 0;
250 }
251 
252 static int
do_ioctl(void * data)253 do_ioctl(void *data)
254 {
255     int fd, ret, saved_errno;
256     fd = open(afs_ioctlpath, O_RDWR);
257     if (fd < 0) {
258 	errno = EINVAL;
259 	return -1;
260     }
261     ret = ioctl(fd, afs_ioctlnum, data);
262     saved_errno = errno;
263     close(fd);
264     errno = saved_errno;
265     return ret;
266 }
267 
268 int
k_pioctl(char * a_path,int o_opcode,struct ViceIoctl * a_paramsP,int a_followSymlinks)269 k_pioctl(char *a_path,
270 	 int o_opcode,
271 	 struct ViceIoctl *a_paramsP,
272 	 int a_followSymlinks)
273 {
274 #ifndef NO_AFS
275     switch(afs_entry_point){
276 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
277     case SINGLE_ENTRY_POINT:
278     case SINGLE_ENTRY_POINT2:
279     case SINGLE_ENTRY_POINT3:
280 	return syscall(afs_syscalls[0], AFSCALL_PIOCTL,
281 		       a_path, o_opcode, a_paramsP, a_followSymlinks);
282 #endif
283 #if defined(AFS_PIOCTL)
284     case MULTIPLE_ENTRY_POINT:
285 	return syscall(afs_syscalls[0],
286 		       a_path, o_opcode, a_paramsP, a_followSymlinks);
287 #endif
288     case LINUX_PROC_POINT: {
289 	struct procdata data = { 0, 0, 0, 0, AFSCALL_PIOCTL };
290 	data.param1 = (unsigned long)a_path;
291 	data.param2 = (unsigned long)o_opcode;
292 	data.param3 = (unsigned long)a_paramsP;
293 	data.param4 = (unsigned long)a_followSymlinks;
294 	return do_ioctl(&data);
295     }
296     case MACOS_DEV_POINT: {
297 	struct devdata data = { AFSCALL_PIOCTL, 0, 0, 0, 0, 0, 0, 0 };
298 	int ret;
299 
300 	data.param1 = (unsigned long)a_path;
301 	data.param2 = (unsigned long)o_opcode;
302 	data.param3 = (unsigned long)a_paramsP;
303 	data.param4 = (unsigned long)a_followSymlinks;
304 
305 	ret = do_ioctl(&data);
306 	if (ret)
307 	    return ret;
308 
309 	return data.retval;
310     }
311     case SUN_PROC_POINT: {
312 	struct sundevdata data = { 0, 0, 0, 0, 0, 0, AFSCALL_PIOCTL };
313 	data.param1 = (unsigned long)a_path;
314 	data.param2 = (unsigned long)o_opcode;
315 	data.param3 = (unsigned long)a_paramsP;
316 	data.param4 = (unsigned long)a_followSymlinks;
317 	return do_ioctl(&data);
318     }
319 #ifdef _AIX
320     case AIX_ENTRY_POINTS:
321 	return Pioctl(a_path, o_opcode, a_paramsP, a_followSymlinks);
322 #endif
323     }
324     errno = ENOSYS;
325 #ifdef SIGSYS
326     kill(getpid(), SIGSYS);	/* You lose! */
327 #endif
328 #endif /* NO_AFS */
329     return -1;
330 }
331 
332 int
k_afs_cell_of_file(const char * path,char * cell,int len)333 k_afs_cell_of_file(const char *path, char *cell, int len)
334 {
335     struct ViceIoctl parms;
336     parms.in = NULL;
337     parms.in_size = 0;
338     parms.out = cell;
339     parms.out_size = len;
340     return k_pioctl(rk_UNCONST(path), VIOC_FILE_CELL_NAME, &parms, 1);
341 }
342 
343 int
k_unlog(void)344 k_unlog(void)
345 {
346     struct ViceIoctl parms;
347     memset(&parms, 0, sizeof(parms));
348     return k_pioctl(0, VIOCUNLOG, &parms, 0);
349 }
350 
351 int
k_setpag(void)352 k_setpag(void)
353 {
354 #ifndef NO_AFS
355     switch(afs_entry_point){
356 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
357     case SINGLE_ENTRY_POINT:
358     case SINGLE_ENTRY_POINT2:
359     case SINGLE_ENTRY_POINT3:
360 	return syscall(afs_syscalls[0], AFSCALL_SETPAG);
361 #endif
362 #if defined(AFS_PIOCTL)
363     case MULTIPLE_ENTRY_POINT:
364 	return syscall(afs_syscalls[1]);
365 #endif
366     case LINUX_PROC_POINT: {
367 	struct procdata data = { 0, 0, 0, 0, AFSCALL_SETPAG };
368 	return do_ioctl(&data);
369     }
370     case MACOS_DEV_POINT: {
371 	struct devdata data = { AFSCALL_SETPAG, 0, 0, 0, 0, 0, 0, 0 };
372 	int ret = do_ioctl(&data);
373 	if (ret)
374 	    return ret;
375 	return data.retval;
376      }
377     case SUN_PROC_POINT: {
378 	struct sundevdata data = { 0, 0, 0, 0, 0, 0, AFSCALL_SETPAG };
379 	return do_ioctl(&data);
380     }
381 #ifdef _AIX
382     case AIX_ENTRY_POINTS:
383 	return Setpag();
384 #endif
385     }
386 
387     errno = ENOSYS;
388 #ifdef SIGSYS
389     kill(getpid(), SIGSYS);	/* You lose! */
390 #endif
391 #endif /* NO_AFS */
392     return -1;
393 }
394 
395 static jmp_buf catch_SIGSYS;
396 
397 #ifdef SIGSYS
398 
399 static RETSIGTYPE
SIGSYS_handler(int sig)400 SIGSYS_handler(int sig)
401 {
402     errno = 0;
403     signal(SIGSYS, SIGSYS_handler); /* Need to reinstall handler on SYSV */
404     longjmp(catch_SIGSYS, 1);
405 }
406 
407 #endif
408 
409 /*
410  * Try to see if `syscall' is a pioctl.  Return 0 iff succesful.
411  */
412 
413 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
414 static int
try_one(int syscall_num)415 try_one (int syscall_num)
416 {
417     struct ViceIoctl parms;
418     memset(&parms, 0, sizeof(parms));
419 
420     if (setjmp(catch_SIGSYS) == 0) {
421 	syscall(syscall_num, AFSCALL_PIOCTL,
422 		0, VIOCSETTOK, &parms, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
423 	if (errno == EINVAL) {
424 	    afs_entry_point = SINGLE_ENTRY_POINT;
425 	    afs_syscalls[0] = syscall_num;
426 	    return 0;
427 	}
428     }
429     return 1;
430 }
431 #endif
432 
433 /*
434  * Try to see if `syscall_pioctl' is a pioctl syscall.  Return 0 iff
435  * succesful.
436  *
437  */
438 
439 #ifdef AFS_PIOCTL
440 static int
try_two(int syscall_pioctl,int syscall_setpag)441 try_two (int syscall_pioctl, int syscall_setpag)
442 {
443     struct ViceIoctl parms;
444     memset(&parms, 0, sizeof(parms));
445 
446     if (setjmp(catch_SIGSYS) == 0) {
447 	syscall(syscall_pioctl,
448 		0, VIOCSETTOK, &parms, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
449 	if (errno == EINVAL) {
450 	    afs_entry_point = MULTIPLE_ENTRY_POINT;
451 	    afs_syscalls[0] = syscall_pioctl;
452 	    afs_syscalls[1] = syscall_setpag;
453 	    return 0;
454 	}
455     }
456     return 1;
457 }
458 #endif
459 
460 int
k_hasafs(void)461 k_hasafs(void)
462 {
463 #if !defined(NO_AFS) && defined(SIGSYS)
464     RETSIGTYPE (*saved_func)(int);
465 #endif
466     int saved_errno, ret;
467     char *env = NULL;
468 
469     if (!issuid())
470 	env = getenv ("AFS_SYSCALL");
471 
472     /*
473      * Already checked presence of AFS syscalls?
474      */
475     if (afs_entry_point != UNKNOWN_ENTRY_POINT)
476 	return afs_entry_point != NO_ENTRY_POINT;
477 
478     /*
479      * Probe kernel for AFS specific syscalls,
480      * they (currently) come in two flavors.
481      * If the syscall is absent we recive a SIGSYS.
482      */
483     afs_entry_point = NO_ENTRY_POINT;
484 
485     saved_errno = errno;
486 #ifndef NO_AFS
487 #ifdef SIGSYS
488     saved_func = signal(SIGSYS, SIGSYS_handler);
489 #endif
490     if (env && strstr(env, "..") == NULL) {
491 
492 	if (strncmp("/proc/", env, 6) == 0) {
493 	    if (try_ioctlpath(env, VIOC_SYSCALL_PROC, LINUX_PROC_POINT) == 0)
494 		goto done;
495 	}
496 	if (strncmp("/dev/", env, 5) == 0) {
497 #ifdef VIOC_SYSCALL_DEV
498 	    if (try_ioctlpath(env, VIOC_SYSCALL_DEV, MACOS_DEV_POINT) == 0)
499 		goto done;
500 #endif
501 #ifdef VIOC_SYSCALL_DEV_OPENAFS
502 	    if (try_ioctlpath(env,VIOC_SYSCALL_DEV_OPENAFS,MACOS_DEV_POINT) ==0)
503 		goto done;
504 #endif
505 	}
506     }
507 
508     ret = try_ioctlpath("/proc/fs/openafs/afs_ioctl",
509 			VIOC_SYSCALL_PROC, LINUX_PROC_POINT);
510     if (ret == 0)
511 	goto done;
512     ret = try_ioctlpath("/proc/fs/nnpfs/afs_ioctl",
513 			VIOC_SYSCALL_PROC, LINUX_PROC_POINT);
514     if (ret == 0)
515 	goto done;
516 
517 #ifdef VIOC_SYSCALL_DEV_OPENAFS
518     ret = try_ioctlpath("/dev/openafs_ioctl",
519 			VIOC_SYSCALL_DEV_OPENAFS, MACOS_DEV_POINT);
520     if (ret == 0)
521 	goto done;
522 #endif
523 #ifdef VIOC_SYSCALL_DEV
524     ret = try_ioctlpath("/dev/nnpfs_ioctl", VIOC_SYSCALL_DEV, MACOS_DEV_POINT);
525     if (ret == 0)
526 	goto done;
527 #endif
528 #ifdef VIOC_SUN_SYSCALL_DEV
529     ret = try_ioctlpath("/dev/afs", VIOC_SUN_SYSCALL_DEV, SUN_PROC_POINT);
530     if (ret == 0)
531 	goto done;
532 #endif
533 
534 
535 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
536     {
537 	int tmp;
538 
539 	if (env != NULL) {
540 	    if (sscanf (env, "%d", &tmp) == 1) {
541 		if (try_one (tmp) == 0)
542 		    goto done;
543 	    } else {
544 		char *end = NULL;
545 		char *p;
546 		char *s = strdup (env);
547 
548 		if (s != NULL) {
549 		    for (p = strtok_r (s, ",", &end);
550 			 p != NULL;
551 			 p = strtok_r (NULL, ",", &end)) {
552 			if (map_syscall_name_to_number (p, &tmp) == 0)
553 			    if (try_one (tmp) == 0) {
554 				free (s);
555 				goto done;
556 			    }
557 		    }
558 		    free (s);
559 		}
560 	    }
561 	}
562     }
563 #endif /* AFS_SYSCALL || AFS_SYSCALL2 || AFS_SYSCALL3 */
564 
565 #ifdef AFS_SYSCALL
566     if (try_one (AFS_SYSCALL) == 0)
567 	goto done;
568 #endif /* AFS_SYSCALL */
569 
570 #ifdef AFS_PIOCTL
571     {
572 	int tmp[2];
573 
574 	if (env != NULL && sscanf (env, "%d%d", &tmp[0], &tmp[1]) == 2)
575 	    if (try_two (tmp[0], tmp[1]) == 2)
576 		goto done;
577     }
578 #endif /* AFS_PIOCTL */
579 
580 #ifdef AFS_PIOCTL
581     if (try_two (AFS_PIOCTL, AFS_SETPAG) == 0)
582 	goto done;
583 #endif /* AFS_PIOCTL */
584 
585 #ifdef AFS_SYSCALL2
586     if (try_one (AFS_SYSCALL2) == 0)
587 	goto done;
588 #endif /* AFS_SYSCALL2 */
589 
590 #ifdef AFS_SYSCALL3
591     if (try_one (AFS_SYSCALL3) == 0)
592 	goto done;
593 #endif /* AFS_SYSCALL3 */
594 
595 #ifdef _AIX
596 #if 0
597     if (env != NULL) {
598 	char *pos = NULL;
599 	char *pioctl_name;
600 	char *setpag_name;
601 
602 	pioctl_name = strtok_r (env, ", \t", &pos);
603 	if (pioctl_name != NULL) {
604 	    setpag_name = strtok_r (NULL, ", \t", &pos);
605 	    if (setpag_name != NULL)
606 		if (try_aix (pioctl_name, setpag_name) == 0)
607 		    goto done;
608 	}
609     }
610 #endif
611 
612     if(try_aix() == 0)
613 	goto done;
614 #endif
615 
616 
617 done:
618 #ifdef SIGSYS
619     signal(SIGSYS, saved_func);
620 #endif
621 #endif /* NO_AFS */
622     errno = saved_errno;
623     return afs_entry_point != NO_ENTRY_POINT;
624 }
625 
626 int
k_hasafs_recheck(void)627 k_hasafs_recheck(void)
628 {
629     afs_entry_point = UNKNOWN_ENTRY_POINT;
630     return k_hasafs();
631 }
632