heimdal Extend the 'hdb as a keytab' code [HEIMDAL-600]
[heimdal.git] / lib / kafs / afssys.c
blob32c1cdba7f6c1c90a5a69694ad097efb7a479928
1 /*
2 * Copyright (c) 1995 - 2000, 2002, 2004, 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "kafs_locl.h"
36 RCSID("$Id$");
38 struct procdata {
39 unsigned long param4;
40 unsigned long param3;
41 unsigned long param2;
42 unsigned long param1;
43 unsigned long syscall;
45 #define VIOC_SYSCALL_PROC _IOW('C', 1, void *)
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;
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
63 int _kafs_debug; /* this should be done in a better way */
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
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;
80 /* Magic to get AIX syscalls to work */
81 #ifdef _AIX
83 static int (*Pioctl)(char*, int, struct ViceIoctl*, int);
84 static int (*Setpag)(void);
86 #include "dlfcn.h"
92 static int
93 try_aix(void)
95 #ifdef STATIC_AFS_SYSCALLS
96 Pioctl = aix_pioctl;
97 Setpag = aix_setpag;
98 #else
99 void *ptr;
100 char path[MaxPathLen], *p;
102 * If we are root or running setuid don't trust AFSLIBPATH!
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);
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));
117 return 1;
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;
126 #endif /* _AIX */
129 * This probably only works under Solaris and could get confused if
130 * there's a /etc/name_to_sysnum file.
133 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
135 #define _PATH_ETC_NAME_TO_SYSNUM "/etc/name_to_sysnum"
137 static int
138 map_syscall_name_to_number (const char *str, int *res)
140 FILE *f;
141 char buf[256];
142 size_t str_len = strlen (str);
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;
151 if (strncmp (str, buf, str_len) == 0) {
152 char *begptr = buf + str_len;
153 char *endptr;
154 long val = strtol (begptr, &endptr, 0);
156 if (val != 0 && endptr != begptr) {
157 fclose (f);
158 *res = val;
159 return 0;
163 fclose (f);
164 return -1;
166 #endif
168 static int
169 try_ioctlpath(const char *path, unsigned long ioctlnum, int entrypoint)
171 int fd, ret, saved_errno;
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;
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;
189 default:
190 abort();
192 saved_errno = errno;
193 close(fd);
195 * Be quite liberal in what error are ok, the first is the one
196 * that should trigger given that params is NULL.
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;
211 static int
212 do_ioctl(void *data)
214 int fd, ret, saved_errno;
215 fd = open(afs_ioctlpath, O_RDWR);
216 if (fd < 0) {
217 errno = EINVAL;
218 return -1;
220 ret = ioctl(fd, afs_ioctlnum, data);
221 saved_errno = errno;
222 close(fd);
223 errno = saved_errno;
224 return ret;
228 k_pioctl(char *a_path,
229 int o_opcode,
230 struct ViceIoctl *a_paramsP,
231 int a_followSymlinks)
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);
255 case MACOS_DEV_POINT: {
256 struct devdata data = { AFSCALL_PIOCTL, 0, 0, 0, 0, 0, 0, 0 };
257 int ret;
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;
264 ret = do_ioctl(&data);
265 if (ret)
266 return ret;
268 return data.retval;
270 #ifdef _AIX
271 case AIX_ENTRY_POINTS:
272 return Pioctl(a_path, o_opcode, a_paramsP, a_followSymlinks);
273 #endif
275 errno = ENOSYS;
276 #ifdef SIGSYS
277 kill(getpid(), SIGSYS); /* You lose! */
278 #endif
279 #endif /* NO_AFS */
280 return -1;
284 k_afs_cell_of_file(const char *path, char *cell, int len)
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);
295 k_unlog(void)
297 struct ViceIoctl parms;
298 memset(&parms, 0, sizeof(parms));
299 return k_pioctl(0, VIOCUNLOG, &parms, 0);
303 k_setpag(void)
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);
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;
328 #ifdef _AIX
329 case AIX_ENTRY_POINTS:
330 return Setpag();
331 #endif
334 errno = ENOSYS;
335 #ifdef SIGSYS
336 kill(getpid(), SIGSYS); /* You lose! */
337 #endif
338 #endif /* NO_AFS */
339 return -1;
342 static jmp_buf catch_SIGSYS;
344 #ifdef SIGSYS
346 static RETSIGTYPE
347 SIGSYS_handler(int sig)
349 errno = 0;
350 signal(SIGSYS, SIGSYS_handler); /* Need to reinstall handler on SYSV */
351 longjmp(catch_SIGSYS, 1);
354 #endif
357 * Try to see if `syscall' is a pioctl. Return 0 iff succesful.
360 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
361 static int
362 try_one (int syscall_num)
364 struct ViceIoctl parms;
365 memset(&parms, 0, sizeof(parms));
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;
376 return 1;
378 #endif
381 * Try to see if `syscall_pioctl' is a pioctl syscall. Return 0 iff
382 * succesful.
386 #ifdef AFS_PIOCTL
387 static int
388 try_two (int syscall_pioctl, int syscall_setpag)
390 struct ViceIoctl parms;
391 memset(&parms, 0, sizeof(parms));
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;
403 return 1;
405 #endif
408 k_hasafs(void)
410 #if !defined(NO_AFS) && defined(SIGSYS)
411 RETSIGTYPE (*saved_func)(int);
412 #endif
413 int saved_errno, ret;
414 char *env = NULL;
416 if (!issuid())
417 env = getenv ("AFS_SYSCALL");
420 * Already checked presence of AFS syscalls?
422 if (afs_entry_point != UNKNOWN_ENTRY_POINT)
423 return afs_entry_point != NO_ENTRY_POINT;
426 * Probe kernel for AFS specific syscalls,
427 * they (currently) come in two flavors.
428 * If the syscall is absent we recive a SIGSYS.
430 afs_entry_point = NO_ENTRY_POINT;
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) {
439 if (strncmp("/proc/", env, 6) == 0) {
440 if (try_ioctlpath(env, VIOC_SYSCALL_PROC, LINUX_PROC_POINT) == 0)
441 goto done;
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
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;
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
476 #if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
478 int tmp;
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);
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;
499 free (s);
504 #endif /* AFS_SYSCALL || AFS_SYSCALL2 || AFS_SYSCALL3 */
506 #ifdef AFS_SYSCALL
507 if (try_one (AFS_SYSCALL) == 0)
508 goto done;
509 #endif /* AFS_SYSCALL */
511 #ifdef AFS_PIOCTL
513 int tmp[2];
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;
519 #endif /* AFS_PIOCTL */
521 #ifdef AFS_PIOCTL
522 if (try_two (AFS_PIOCTL, AFS_SETPAG) == 0)
523 goto done;
524 #endif /* AFS_PIOCTL */
526 #ifdef AFS_SYSCALL2
527 if (try_one (AFS_SYSCALL2) == 0)
528 goto done;
529 #endif /* AFS_SYSCALL2 */
531 #ifdef AFS_SYSCALL3
532 if (try_one (AFS_SYSCALL3) == 0)
533 goto done;
534 #endif /* AFS_SYSCALL3 */
536 #ifdef _AIX
537 #if 0
538 if (env != NULL) {
539 char *pos = NULL;
540 char *pioctl_name;
541 char *setpag_name;
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;
551 #endif
553 if(try_aix() == 0)
554 goto done;
555 #endif
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;
568 k_hasafs_recheck(void)
570 afs_entry_point = UNKNOWN_ENTRY_POINT;
571 return k_hasafs();