1/* $NetBSD$ */ 2 3/* 4 * lib/krb5/os/k5dfspag.c 5 * 6 * New Kerberos module to issue the DFS PAG syscalls. 7 * It also contains the routine to fork and exec the 8 * k5dcecon routine to do most of the work. 9 * 10 * This file is designed to be as independent of DCE 11 * and DFS as possible. The only dependencies are on 12 * the syscall numbers. If DFS not running or not installed, 13 * the sig handlers will catch and the signal and 14 * will continue. 15 * 16 * krb5_dfs_newpag and krb5_dfs_getpag should not be real 17 * Kerberos routines, since they should be setpag and getpag 18 * in the DCE library, but without the DCE baggage. 19 * Thus they don't have context, and don't return a krb5 error. 20 * 21 * 22 * 23 * krb5_dfs_pag() 24 */ 25 26#ifdef HAVE_CONFIG_H 27#include <config.h> 28#endif 29 30__RCSID("$NetBSD$"); 31 32#include <krb5/krb5.h> 33 34#ifdef DCE 35 36#include <stdio.h> 37#include <sys/stat.h> 38#include <sys/wait.h> 39#include <fcntl.h> 40#include <sys/param.h> 41 42/* Only run this DFS PAG code on systems with POSIX 43 * All that we are interested in dor:, AIX 4.x, 44 * Solaris 2.5.x, HPUX 10.x Even SunOS 4.1.4, AIX 3.2.5 45 * and SGI 5.3 are OK. This simplifies 46 * the build/configure which I don't want to change now. 47 * All of them also have waitpid as well. 48 */ 49 50#define POSIX_SETJMP 51#define POSIX_SIGNALS 52#define HAVE_WAITPID 53 54#include <signal.h> 55#include <setjmp.h> 56#ifndef POSIX_SETJMP 57#undef sigjmp_buf 58#undef sigsetjmp 59#undef siglongjmp 60#define sigjmp_buf jmp_buf 61#define sigsetjmp(j,s) setjmp(j) 62#define siglongjmp longjmp 63#endif 64 65#ifdef POSIX_SIGNALS 66typedef struct sigaction handler; 67#define handler_init(H,F) (sigemptyset(&(H).sa_mask), \ 68 (H).sa_flags=0, \ 69 (H).sa_handler=(F)) 70#define handler_swap(S,NEW,OLD) sigaction(S, &NEW, &OLD) 71#define handler_set(S,OLD) sigaction(S, &OLD, NULL) 72#else 73typedef sigtype (*handler)(); 74#define handler_init(H,F) ((H) = (F)) 75#define handler_swap(S,NEW,OLD) ((OLD) = signal ((S), (NEW))) 76#define handler_set(S,OLD) (signal ((S), (OLD))) 77#endif 78 79#define krb5_sigtype void 80#define WAIT_USES_INT 81typedef krb5_sigtype sigtype; 82 83 84/* 85 * Need some syscall numbers based on different systems. 86 * These are based on: 87 * HPUX 10.10 /opt/dce/include/dcedfs/syscall.h 88 * Solaris 2.5 /opt/dcelocal/share/include/dcedfs/syscall.h 89 * AIX 4.2 - needs some funny games with load and kafs_syscall 90 * to get the kernel extentions. There should be a better way! 91 * 92 * DEE 5/27/97 93 * 94 */ 95 96 97#define AFSCALL_SETPAG 2 98#define AFSCALL_GETPAG 11 99 100#if defined(sun) 101#define AFS_SYSCALL 72 102 103#elif defined(hpux) 104/* assume HPUX 10 + or is it 50 */ 105#define AFS_SYSCALL 326 106 107#elif defined(_AIX) 108#ifndef DPAGAIX 109#define DPAGAIX LIBEXECDIR "/dpagaix" 110#endif 111int *load(); 112static int (*dpagaix)(int, int, int, int, int, int) = 0; 113 114#elif defined(sgi) || defined(_sgi) 115#define AFS_SYSCALL 206+1000 116 117#else 118#define AFS_SYSCALL (Unknown_DFS_AFS_SYSCALL) 119#endif 120 121 122#ifdef WAIT_USES_INT 123 int wait_status; 124#else /* WAIT_USES_INT */ 125 union wait wait_status; 126#endif /* WAIT_USES_INT */ 127 128#ifndef K5DCECON 129#define K5DCECON LIBEXECDIR "/k5dcecon" 130#endif 131 132/* 133 * mysig() 134 * 135 * signal handler if DFS not running 136 * 137 */ 138 139static sigjmp_buf setpag_buf; 140 141static sigtype mysig() 142{ 143 siglongjmp(setpag_buf, 1); 144} 145 146/* 147 * krb5_dfs_pag_syscall() 148 * 149 * wrapper for the syscall with signal handlers 150 * 151 */ 152 153static int krb5_dfs_pag_syscall(opt1,opt2) 154 int opt1; 155 int opt2; 156{ 157 handler sa1, osa1; 158 handler sa2, osa2; 159 int pag = -2; 160 161 handler_init (sa1, mysig); 162 handler_init (sa2, mysig); 163 handler_swap (SIGSYS, sa1, osa1); 164 handler_swap (SIGSEGV, sa2, osa2); 165 166 if (sigsetjmp(setpag_buf, 1) == 0) { 167 168#if defined(_AIX) 169 if (!dpagaix) 170 dpagaix = load(DPAGAIX, 0, 0); 171 if (dpagaix) 172 pag = (*dpagaix)(opt1, opt2, 0, 0, 0, 0); 173#else 174 pag = syscall(AFS_SYSCALL, opt1, opt2, 0, 0, 0, 0); 175#endif 176 177 handler_set (SIGSYS, osa1); 178 handler_set (SIGSEGV, osa2); 179 return(pag); 180 } 181 182 /* syscall failed! return 0 */ 183 handler_set (SIGSYS, osa1); 184 handler_set (SIGSEGV, osa2); 185 return(-2); 186} 187 188/* 189 * krb5_dfs_newpag() 190 * 191 * issue a DCE/DFS setpag system call to set the newpag 192 * for this process. This takes advantage of a currently 193 * undocumented feature of the Transarc port of DFS. 194 * Even in DCE 1.2.2 for which the source is available, 195 * (but no vendors have released), this feature is not 196 * there, but it should be, or could be added. 197 * If new_pag is zero, then the syscall will get a new pag 198 * and return its value. 199 */ 200 201int krb5_dfs_newpag(new_pag) 202 int new_pag; 203{ 204 return(krb5_dfs_pag_syscall(AFSCALL_SETPAG, new_pag)); 205} 206 207/* 208 * krb5_dfs_getpag() 209 * 210 * get the current PAG. Used mostly as a test. 211 */ 212 213int krb5_dfs_getpag() 214{ 215 return(krb5_dfs_pag_syscall(AFSCALL_GETPAG, 0)); 216} 217 218/* 219 * krb5_dfs_pag() 220 * 221 * Given a principal and local username, 222 * fork and exec the k5dcecon module to create 223 * refresh or join a new DCE/DFS 224 * Process Authentication Group (PAG) 225 * 226 * This routine should be called after krb5_kuserok has 227 * determined that this combination of local user and 228 * principal are acceptable for the local host. 229 * 230 * It should also be called after a forwarded ticket has 231 * been received, and the KRB5CCNAME environment variable 232 * has been set to point at it. k5dcecon will convert this 233 * to a new DCE context and a new pag and replace KRB5CCNAME 234 * in the environment. 235 * 236 * If there is no forwarded ticket, k5dcecon will attempt 237 * to join an existing PAG for the same principal and local 238 * user. 239 * 240 * And it should be called before access to the home directory 241 * as this may be in DFS, not accessable by root, and require 242 * the PAG to have been setup. 243 * 244 * The krb5_afs_pag can be called after this routine to 245 * use the the cache obtained by k5dcecon to get an AFS token. 246 * DEE - 7/97 247 */ 248 249int krb5_dfs_pag(context, flag, principal, luser) 250 krb5_context context; 251 int flag; /* 1 if a forwarded TGT is to be used */ 252 krb5_principal principal; 253 const char *luser; 254 255{ 256 257 struct stat stx; 258 int fd[2]; 259 int i,j; 260 int pid; 261 int new_pag; 262 int pag; 263 char newccname[MAXPATHLEN] = ""; 264 char *princ; 265 int err; 266 struct sigaction newsig, oldsig; 267 268#ifdef WAIT_USES_INT 269 int wait_status; 270#else /* WAIT_USES_INT */ 271 union wait wait_status; 272#endif /* WAIT_USES_INT */ 273 274 if (krb5_unparse_name(context, principal, &princ)) 275 return(0); 276 277 /* test if DFS is running or installed */ 278 if (krb5_dfs_getpag() == -2) 279 return(0); /* DFS not running, dont try */ 280 281 if (pipe(fd) == -1) 282 return(0); 283 284 /* Make sure that telnetd.c's SIGCHLD action don't happen right now... */ 285 memset((char *)&newsig, 0, sizeof(newsig)); 286 newsig.sa_handler = SIG_DFL; 287 sigaction(SIGCHLD, &newsig, &oldsig); 288 289 pid = fork(); 290 if (pid <0) 291 return(0); 292 293 if (pid == 0) { /* child process */ 294 295 close(1); /* close stdout */ 296 dup(fd[1]); /* point stdout at pipe here */ 297 close(fd[0]); /* don't use end of pipe here */ 298 close(fd[1]); /* pipe now as stdout */ 299 300 execl(K5DCECON, "k5dcecon", 301 (flag) ? "-f" : "-s" , 302 "-l", luser, 303 "-p", princ, (char *)0); 304 305 exit(127); /* incase execl fails */ 306 } 307 308 /* parent, wait for child to finish */ 309 310 close(fd[1]); /* dont need this end of pipe */ 311 312/* #if defined(sgi) || defined(_sgi) */ 313 /* wait_status.w_status = 0; */ 314 /* waitpid((pid_t) pid, &wait_status.w_status, 0); */ 315/* #else */ 316 317 318 wait_status = 0; 319#ifdef HAVE_WAITPID 320 err = waitpid((pid_t) pid, &wait_status, 0); 321#else /* HAVE_WAITPID */ 322 err = wait4(pid, &wait_status, 0, (struct rusage *) NULL); 323#endif /* HAVE_WAITPID */ 324/* #endif */ 325 326 sigaction(SIGCHLD, &oldsig, 0); 327 if (WIFEXITED(wait_status)){ 328 if (WEXITSTATUS(wait_status) == 0) { 329 i = 1; 330 j = 0; 331 while (i != 0) { 332 i = read(fd[0], &newccname[j], sizeof(newccname)-1-j); 333 if ( i > 0) 334 j += i; 335 if (j >= sizeof(newccname)-1) 336 i = 0; 337 } 338 close(fd[0]); 339 if (j > 0) { 340 newccname[j] = '\0'; 341 esetenv("KRB5CCNAME",newccname,1); 342 sscanf(&newccname[j-8],"%8x",&new_pag); 343 if (new_pag && strncmp("FILE:/opt/dcelocal/var/security/creds/dcecred_", newccname, 46) == 0) { 344 if((pag = krb5_dfs_newpag(new_pag)) != -2) { 345 return(pag); 346 } 347 } 348 } 349 } 350 } 351 return(0); /* something not right */ 352} 353 354#else /* DCE */ 355 356/* 357 * krb5_dfs_pag - dummy version for the lib for systems 358 * which don't have DFS, or the needed setpag kernel code. 359 */ 360 361krb5_boolean 362krb5_dfs_pag(context, principal, luser) 363 krb5_context context; 364 krb5_principal principal; 365 const char *luser; 366{ 367 return(0); 368} 369 370#endif /* DCE */ 371