s4:heimdal: import lorikeet-heimdal-202201172009 (commit 5a0b45cd723628b3690ea848548b...
[samba.git] / source4 / heimdal / lib / kdfs / k5dfspag.c
diff --git a/source4/heimdal/lib/kdfs/k5dfspag.c b/source4/heimdal/lib/kdfs/k5dfspag.c
new file mode 100644 (file)
index 0000000..9db2555
--- /dev/null
@@ -0,0 +1,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 */