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