summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/kdfs/k5dfspag.c
blob: 9db2555d051d99348b1d9d2abdd9433f44dad96e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/*
 * lib/krb5/os/k5dfspag.c
 *
 * New Kerberos module to issue the DFS PAG syscalls.
 * It also contains the routine to fork and exec the
 * k5dcecon routine to do most of the work.
 *
 * This file is designed to be as independent of DCE
 * and DFS as possible. The only dependencies are on
 * the syscall numbers.  If DFS not running or not installed,
 * the sig handlers will catch and the signal and
 * will  continue.
 *
 * krb5_dfs_newpag and krb5_dfs_getpag should not be real
 * Kerberos routines, since they should be setpag and getpag
 * in the DCE library, but without the DCE baggage.
 * Thus they don't have context, and don't return a krb5 error.
 *
 *
 *
 * krb5_dfs_pag()
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

RCSID("$Id$");

#include <krb5.h>

#ifdef DCE

#include <stdio.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/param.h>

/* Only run this DFS PAG code on systems with POSIX
 * All that we are interested in dor:, AIX 4.x,
 * Solaris 2.5.x, HPUX 10.x  Even SunOS 4.1.4, AIX 3.2.5
 * and SGI 5.3 are OK.  This simplifies
 * the build/configure which I don't want to change now.
 * All of them also have waitpid as well.
 */

#define POSIX_SETJMP
#define POSIX_SIGNALS
#define HAVE_WAITPID

#include <signal.h>
#include <setjmp.h>
#ifndef POSIX_SETJMP
#undef sigjmp_buf
#undef sigsetjmp
#undef siglongjmp
#define sigjmp_buf  jmp_buf
#define sigsetjmp(j,s)  setjmp(j)
#define siglongjmp  longjmp
#endif

#ifdef POSIX_SIGNALS
typedef struct sigaction handler;
#define handler_init(H,F)       (sigemptyset(&(H).sa_mask), \
                     (H).sa_flags=0, \
                     (H).sa_handler=(F))
#define handler_swap(S,NEW,OLD)     sigaction(S, &NEW, &OLD)
#define handler_set(S,OLD)      sigaction(S, &OLD, NULL)
#else
typedef sigtype (*handler)();
#define handler_init(H,F)       ((H) = (F))
#define handler_swap(S,NEW,OLD)     ((OLD) = signal ((S), (NEW)))
#define handler_set(S,OLD)      (signal ((S), (OLD)))
#endif

#define krb5_sigtype void
#define WAIT_USES_INT
typedef krb5_sigtype sigtype;


/*
 * Need some syscall numbers based on different systems.
 * These are based on:
 * HPUX 10.10 /opt/dce/include/dcedfs/syscall.h
 * Solaris 2.5 /opt/dcelocal/share/include/dcedfs/syscall.h
 * AIX 4.2  - needs some funny games with load and kafs_syscall
 * to get the kernel extentions. There should be a better way!
 *
 * DEE 5/27/97
 *
 */


#define AFSCALL_SETPAG 2
#define AFSCALL_GETPAG 11

#if defined(sun)
#define AFS_SYSCALL  72

#elif defined(hpux)
/* assume HPUX 10 +  or is it 50 */
#define AFS_SYSCALL 326

#elif defined(_AIX)
#ifndef DPAGAIX
#define DPAGAIX LIBEXECDIR "/dpagaix"
#endif
int *load();
static int (*dpagaix)(int, int, int, int, int, int) = 0;

#elif defined(sgi) || defined(_sgi)
#define AFS_SYSCALL      206+1000

#else
#define AFS_SYSCALL (Unknown_DFS_AFS_SYSCALL)
#endif


#ifdef  WAIT_USES_INT
                int wait_status;
#else   /* WAIT_USES_INT */
                union wait wait_status;
#endif  /* WAIT_USES_INT */

#ifndef K5DCECON
#define K5DCECON LIBEXECDIR "/k5dcecon"
#endif

/*
 * mysig()
 *
 * signal handler if DFS not running
 *
 */

static sigjmp_buf setpag_buf;

static sigtype mysig()
{
  siglongjmp(setpag_buf, 1);
}

/*
 * krb5_dfs_pag_syscall()
 *
 * wrapper for the syscall with signal handlers
 *
 */

static int  krb5_dfs_pag_syscall(opt1,opt2)
  int opt1;
  int opt2;
{
  handler sa1, osa1;
  handler sa2, osa2;
  int pag = -2;

  handler_init (sa1, mysig);
  handler_init (sa2, mysig);
  handler_swap (SIGSYS, sa1, osa1);
  handler_swap (SIGSEGV, sa2, osa2);

  if (sigsetjmp(setpag_buf, 1) == 0) {

#if defined(_AIX)
    if (!dpagaix)
      dpagaix = load(DPAGAIX, 0, 0);
    if (dpagaix)
      pag = (*dpagaix)(opt1, opt2, 0, 0, 0, 0);
#else
    pag = syscall(AFS_SYSCALL, opt1, opt2, 0, 0, 0, 0);
#endif

    handler_set (SIGSYS, osa1);
    handler_set (SIGSEGV, osa2);
    return(pag);
  }

  /* syscall failed! return 0 */
  handler_set (SIGSYS, osa1);
  handler_set (SIGSEGV, osa2);
  return(-2);
}

/*
 * krb5_dfs_newpag()
 *
 * issue a DCE/DFS setpag system call to set the newpag
 * for this process. This takes advantage of a currently
 * undocumented feature of the Transarc port of DFS.
 * Even in DCE 1.2.2 for which the source is available,
 * (but no vendors have released), this feature is not
 * there, but it should be, or could be added.
 * If new_pag is zero, then the syscall will get a new pag
 * and return its value.
 */

int krb5_dfs_newpag(new_pag)
  int new_pag;
{
  return(krb5_dfs_pag_syscall(AFSCALL_SETPAG, new_pag));
}

/*
 * krb5_dfs_getpag()
 *
 * get the current PAG. Used mostly as a test.
 */

int krb5_dfs_getpag()
{
  return(krb5_dfs_pag_syscall(AFSCALL_GETPAG, 0));
}

/*
 * krb5_dfs_pag()
 *
 * Given a principal and local username,
 * fork and exec the k5dcecon module to create
 * refresh or join a new DCE/DFS
 * Process Authentication Group (PAG)
 *
 * This routine should be called after krb5_kuserok has
 * determined that this combination of local user and
 * principal are acceptable for the local host.
 *
 * It should also be called after a forwarded ticket has
 * been received, and the KRB5CCNAME environment variable
 * has been set to point at it. k5dcecon will convert this
 * to a new DCE context and a new pag and replace KRB5CCNAME
 * in the environment.
 *
 * If there is no forwarded ticket, k5dcecon will attempt
 * to join an existing PAG for the same principal and local
 * user.
 *
 * And it should be called before access to the home directory
 * as this may be in DFS, not accessible by root, and require
 * the PAG to have been setup.
 *
 * The krb5_afs_pag can be called after this routine to
 * use the the cache obtained by k5dcecon to get an AFS token.
 * DEE - 7/97
 */

int krb5_dfs_pag(context, flag, principal, luser)
	krb5_context context;
    int flag; /* 1 if a forwarded TGT is to be used */
	krb5_principal principal;
	const char *luser;

{

  struct stat stx;
  int fd[2];
  int i,j;
  int pid;
  int new_pag;
  int pag;
  char newccname[MAXPATHLEN] = "";
  char *princ;
  int err;
  struct sigaction newsig, oldsig;

#ifdef  WAIT_USES_INT
  int wait_status;
#else   /* WAIT_USES_INT */
  union wait wait_status;
#endif  /* WAIT_USES_INT */

  if (krb5_unparse_name(context, principal, &princ))
   return(0);

   /* test if DFS is running or installed */
   if (krb5_dfs_getpag() == -2)
     return(0); /* DFS not running, don't try */

  if (pipe(fd) == -1)
     return(0);

  /* Make sure that telnetd.c's SIGCHLD action don't happen right now... */
  memset((char *)&newsig, 0, sizeof(newsig));
  newsig.sa_handler = SIG_DFL;
  sigaction(SIGCHLD, &newsig, &oldsig);

  pid = fork();
  if (pid <0)
   return(0);

  if (pid == 0) {  /* child process */

    close(1);       /* close stdout */
    dup(fd[1]);     /* point stdout at pipe here */
    close(fd[0]);   /* don't use end of pipe here */
    close(fd[1]);   /* pipe now as stdout */

    execl(K5DCECON, "k5dcecon",
         (flag) ? "-f" : "-s" ,
		 "-l", luser,
		 "-p", princ, (char *)0);

    exit(127);      /* incase execl fails */
  }

  /* parent, wait for child to finish */

  close(fd[1]);  /* don't need this end of pipe */

/* #if defined(sgi) || defined(_sgi) */
  /* wait_status.w_status = 0; */
  /* waitpid((pid_t) pid, &wait_status.w_status, 0); */
/* #else */


  wait_status = 0;
#ifdef  HAVE_WAITPID
  err = waitpid((pid_t) pid, &wait_status, 0);
#else   /* HAVE_WAITPID */
  err = wait4(pid, &wait_status, 0, (struct rusage *) NULL);
#endif  /* HAVE_WAITPID */
/* #endif */

  sigaction(SIGCHLD, &oldsig, 0);
  if (WIFEXITED(wait_status)){
    if (WEXITSTATUS(wait_status) == 0) {
      i = 1;
      j = 0;
      while (i != 0) {
        i = read(fd[0], &newccname[j], sizeof(newccname)-1-j);
        if ( i > 0)
          j += i;
        if (j >=  sizeof(newccname)-1)
          i = 0;
      }
      close(fd[0]);
      if (j > 0) {
        newccname[j] = '\0';
        esetenv("KRB5CCNAME",newccname,1);
        sscanf(&newccname[j-8],"%8x",&new_pag);
        if (new_pag && strncmp("FILE:/opt/dcelocal/var/security/creds/dcecred_", newccname, 46) == 0) {
          if((pag = krb5_dfs_newpag(new_pag)) != -2) {
            return(pag);
          }
        }
      }
    }
  }
  return(0); /* something not right */
}

#else /* DCE */

/*
 * krb5_dfs_pag - dummy version for the lib for systems
 * which don't have DFS, or the needed setpag kernel code.
 */

krb5_boolean
krb5_dfs_pag(context, principal, luser)
	krb5_context context;
	krb5_principal principal;
	const char *luser;
{
	return(0);
}

#endif /* DCE */