--- /dev/null
+/*
+ * userinit2 - Launch programs as user during boot (Kerberized v2)
+ * Copyright (C) 2006 Fredrik Tolf (fredrik@dolda2000.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <krb5.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+krb5_context ctx;
+static int credsfwd;
+static int credsrnw;
+
+static int parsetime(char *ts)
+{
+ char *p;
+ int unit;
+
+ p = ts + strlen(ts) - 1;
+ unit = 1;
+ if(*p == 'm')
+ unit = 60;
+ else if(*p == 'h')
+ unit = 3600;
+ else if(*p == 'd')
+ unit = 86400;
+ return(atoi(ts) * unit);
+}
+
+static void getcreds(struct passwd *pw, krb5_keytab kt, krb5_principal prn)
+{
+ int ret, fd;
+ krb5_get_init_creds_opt o;
+ krb5_creds c;
+ krb5_ccache cc;
+ char buf[1024], *ccnm, *fnm;
+
+ krb5_get_init_creds_opt_init(&o);
+ krb5_get_init_creds_opt_set_forwardable(&o, credsfwd);
+ krb5_get_init_creds_opt_set_renew_life(&o, credsrnw);
+ if((ret = krb5_get_init_creds_keytab(ctx, &c, prn, kt, 0, NULL, &o)) != 0) {
+ fprintf(stderr, "userinit2: could not get krb credentials: %s\n", error_message(ret));
+ exit(1);
+ }
+ ccnm = buf + sprintf(buf, "KRB5CCNAME=");
+ fnm = ccnm + sprintf(ccnm, "FILE:");
+ sprintf(fnm, "/tmp/krb5cc_ui_%i_XXXXXX", pw->pw_uid);
+ if((fd = mkstemp(fnm)) < 0) {
+ fprintf(stderr, "userinit2: could not ccache file: %s", strerror(errno));
+ exit(1);
+ }
+ close(fd);
+ if((ret = krb5_cc_resolve(ctx, ccnm, &cc)) != 0) {
+ fprintf(stderr, "userinit2: could not resolve ccache %s: %s", ccnm, error_message(ret));
+ unlink(fnm);
+ exit(1);
+ }
+ if((ret = krb5_cc_initialize(ctx, cc, prn)) != 0) {
+ fprintf(stderr, "userinit2: could not initialize ccache: %s", error_message(ret));
+ unlink(fnm);
+ exit(1);
+ }
+ if((ret = krb5_cc_store_cred(ctx, cc, &c)) != 0) {
+ fprintf(stderr, "userinit2: could not store TGT: %s", error_message(ret));
+ unlink(fnm);
+ exit(1);
+ }
+ putenv(strdup(buf));
+ krb5_cc_close(ctx, cc);
+ krb5_free_cred_contents(ctx, &c);
+ if(chown(fnm, pw->pw_uid, pw->pw_gid)) {
+ fprintf(stderr, "userinit2: could not chown ccache file: %s", strerror(errno));
+ unlink(fnm);
+ exit(1);
+ }
+}
+
+static void dologin(struct passwd *pw)
+{
+ char ebuf[1024];
+
+ if(chdir(pw->pw_dir)) {
+ fprintf(stderr, "userinit2: could not chdir to home directory %s: %s\n", pw->pw_dir, strerror(errno));
+ exit(1);
+ }
+ if(snprintf(ebuf, sizeof(ebuf), "HOME=%s", pw->pw_dir) < sizeof(ebuf))
+ putenv(strdup(ebuf));
+ if(snprintf(ebuf, sizeof(ebuf), "SHELL=%s", pw->pw_shell) < sizeof(ebuf))
+ putenv(strdup(ebuf));
+ if(snprintf(ebuf, sizeof(ebuf), "USER=%s", pw->pw_name) < sizeof(ebuf))
+ putenv(strdup(ebuf));
+ if(snprintf(ebuf, sizeof(ebuf), "LOGNAME=%s", pw->pw_name) < sizeof(ebuf))
+ putenv(strdup(ebuf));
+ if(snprintf(ebuf, sizeof(ebuf), "PATH=%s/bin:/usr/local/bin:/bin:/usr/bin", pw->pw_dir) < sizeof(ebuf))
+ putenv(strdup(ebuf));
+}
+
+static void dodir(void)
+{
+ DIR *dir;
+ struct dirent *de;
+ struct stat sb;
+
+ if((dir = opendir(".")) == NULL) {
+ fprintf(stderr, "userinit2: couldn't open cwd (%s)!\n", strerror(errno));
+ exit(1);
+ }
+ while((de = readdir(dir)) != NULL) {
+ if(de->d_name[0] == '.')
+ continue;
+ if(access(de->d_name, X_OK))
+ continue;
+ if(stat(de->d_name, &sb))
+ continue;
+ if(!S_ISREG(sb.st_mode))
+ continue;
+ if(!fork()) {
+ setpgrp();
+ execl(de->d_name, de->d_name, NULL);
+ exit(127);
+ }
+ }
+ closedir(dir);
+}
+
+static void runstuff(struct passwd *pw)
+{
+ int i, fd1, fd2;
+ char buf[1024];
+
+ if(chdir(".userinit"))
+ return;
+ for(i = 3; i < FD_SETSIZE; i++)
+ close(i);
+ if((fd1 = open("/dev/null", O_RDWR)) < 0) {
+ fprintf(stderr, "userinit2: /dev/null: %s\n", strerror(errno));
+ exit(1);
+ }
+ if((fd2 = open("stderr", O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) {
+ fprintf(stderr, "userinit2: stderr: %s\n", strerror(errno));
+ exit(1);
+ }
+ dup2(fd1, 0);
+ dup2(fd1, 1);
+ dup2(fd2, 2);
+ close(fd1);
+ close(fd2);
+ setsid();
+ dodir();
+ if(gethostname(buf, sizeof(buf)))
+ return;
+ if(chdir(buf))
+ return;
+ dodir();
+}
+
+int main(int argc, char **argv)
+{
+ int ret, c;
+ krb5_keytab kt;
+ krb5_kt_cursor ktc;
+ krb5_keytab_entry kte;
+ struct passwd *pw;
+
+ while((c = getopt(argc, argv, "hfr:")) >= 0) {
+ switch(c) {
+ case 'f':
+ credsfwd = 1;
+ break;
+ case 'r':
+ credsrnw = parsetime(optarg);
+ break;
+ default:
+ fprintf(stderr, "usage: userinit2 [-hf] [-r renewlife]\n");
+ exit((c == 'h')?0:1);
+ }
+ }
+ if((ret = krb5_init_context(&ctx)) != 0) {
+ fprintf(stderr, "userinit2: could not get krb context: %s\n", error_message(ret));
+ exit(1);
+ }
+ if((ret = krb5_kt_default(ctx, &kt)) != 0) {
+ fprintf(stderr, "userinit2: could not get keytab: %s\n", error_message(ret));
+ exit(1);
+ }
+ if((ret = krb5_kt_start_seq_get(ctx, kt, &ktc)) != 0) {
+ fprintf(stderr, "userinit2: could not iterate keytab: %s\n", error_message(ret));
+ exit(1);
+ }
+ while(krb5_kt_next_entry(ctx, kt, &kte, &ktc) == 0) {
+ do {
+ if((kte.principal->length >= 2) && !strcmp(kte.principal->data[1].data, "userinit")) {
+ if((pw = getpwnam(kte.principal->data[0].data)) == NULL)
+ break;
+ if(!(ret = fork())) {
+ getcreds(pw, kt, kte.principal);
+ if(getuid() == 0) {
+ if(initgroups(pw->pw_name, pw->pw_gid)) {
+ fprintf(stderr, "userinit2: initgroups: %s\n", strerror(errno));
+ exit(1);
+ }
+ if(setgid(pw->pw_gid)) {
+ fprintf(stderr, "userinit2: setgid: %s\n", strerror(errno));
+ exit(1);
+ }
+ if(setuid(pw->pw_uid)) {
+ fprintf(stderr, "userinit2: setuid: %s\n", strerror(errno));
+ exit(1);
+ }
+ } else {
+ if(pw->pw_uid != getuid())
+ break;
+ }
+ dologin(pw);
+ runstuff(pw);
+ exit(0);
+ }
+ if(ret < 0) {
+ fprintf(stderr, "userinit2: fork: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ } while(0);
+ krb5_free_keytab_entry_contents(ctx, &kte);
+ }
+ krb5_kt_end_seq_get(ctx, kt, &ktc);
+ krb5_kt_close(ctx, kt);
+ return(0);
+}
+
+/*
+ * Local Variables:
+ * compile-command: "gcc -Wall -g -o userinit2 userinit2.c -lkrb5"
+ * End:
+ */