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
66 typedef 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
73 typedef 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
81 typedef 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
111 int *load();
112 static 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
139 static sigjmp_buf setpag_buf;
140
mysig()141 static 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
krb5_dfs_pag_syscall(opt1,opt2)153 static 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
krb5_dfs_newpag(new_pag)201 int 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
krb5_dfs_getpag()213 int 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
krb5_dfs_pag(context,flag,principal,luser)249 int 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
361 krb5_boolean
krb5_dfs_pag(context,principal,luser)362 krb5_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