1/*	$NetBSD: k5dfspag.c,v 1.3 2019/12/15 22:50:50 christos Exp $	*/
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: k5dfspag.c,v 1.3 2019/12/15 22:50:50 christos Exp $");
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 accessible 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, don't 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]);  /* don't 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