[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

0.6.4 krb4 / kaserver redux



So, turns out that the 0.6.4 KDC's kaserver emulation has the same bug
as the krb4 emulation with respect to looking things up in the database.
(I discovered this when I brought up the new code on one of our
advertised KDCs, and it promptly failed as my boss's boss tried to "klog
admin".  We've been running krb5 for 5 years now, but I think it's going
to be a while before we can retire klog and company....)

Attached are my current patches to the codebase:
- fix krb4 and kaserver principal lookups
- add automatic reauthentication to kinit
  * unlike John Bucy's original patch for 0.6.3, this one saves the
    password in a pipe so it's only in process memory while it's being
    used
  * probably a future version should get a max-renewable-life ticket
    and renew it periodically, instead of this hack
- su now gets (well, 524s) a krb4 ticket as well as the krb5 ticket and
  token
  * ideally this patch will become obsolete here when I put this stuff
    into wide distribution, but during testing I prefer not to need to
    kinit after su in order to connect to older systems

Not included is a hack to configure to try -ldb-4.2 before the other
possible names for the sleepycat db library, since it's not done
"properly".

-- 
brandon s. allbery   [linux,solaris,freebsd,perl]      allbery@kf8nh.com
system administrator      [WAY too many hats]        allbery@ece.cmu.edu
electrical and computer engineering, carnegie mellon univ.         KF8NH
--- heimdal-0.6.4/kdc/kerberos4.c.dist	Fri May 27 13:56:34 2005
+++ heimdal-0.6.4/kdc/kerberos4.c	Fri May 27 13:58:01 2005
@@ -387,16 +387,18 @@
 		  "%s.%s@%s",
 		  ad.pname, ad.pinst, ad.prealm);
 	ret = db_fetch4(ad.pname, ad.pinst, ad.prealm, &client);
-	if(ret != HDB_ERR_NOENTRY || 
-	   (ret == HDB_ERR_NOENTRY && strcmp(ad.prealm, v4_realm) == 0)) {
-	    char *s;
-	    s = kdc_log_msg(0, "Client not found in database: (krb4) "
-			    "%s.%s@%s: %s",
-			    ad.pname, ad.pinst, ad.prealm,
-			    krb5_get_err_text(context, ret));
-	    make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, s);
-	    free(s);
-	    goto out2;
+	if(ret){
+	    if(ret != HDB_ERR_NOENTRY || 
+	       (ret == HDB_ERR_NOENTRY && strcmp(ad.prealm, v4_realm) == 0)) {
+		char *s;
+		s = kdc_log_msg(0, "Client not found in database: (krb4) "
+				"%s.%s@%s: %s",
+				ad.pname, ad.pinst, ad.prealm,
+				krb5_get_err_text(context, ret));
+		make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, s);
+		free(s);
+		goto out2;
+	    }
 	}
 	
 	ret = db_fetch4(sname, sinst, v4_realm, &server);
--- heimdal-0.6.4/kdc/kaserver.c.dist	Tue May 31 12:58:23 2005
+++ heimdal-0.6.4/kdc/kaserver.c	Tue May 31 12:58:37 2005
@@ -698,12 +698,14 @@
 	      "%s.%s@%s", pname, pinst, prealm);
 
     ret = db_fetch4 (pname, pinst, prealm, &client_entry);
-    if(ret != HDB_ERR_NOENTRY || 
-       (ret == HDB_ERR_NOENTRY && strcmp(prealm, v4_realm) == 0)) {
+    if (ret) {
+      if(ret != HDB_ERR_NOENTRY || 
+	 (ret == HDB_ERR_NOENTRY && strcmp(prealm, v4_realm) == 0)) {
 	kdc_log(0, "Client not found in database: %s: %s",
 		client_name, krb5_get_err_text(context, ret));
 	make_error_reply (hdr, KANOENT, reply);
 	goto out;
+      }
     }
 
     ret = check_flags (client_entry, client_name,
diff -ur heimdal-0.6.3-dist/kuser/kinit.c heimdal-0.6.3/kuser/kinit.c
--- heimdal-0.6.3-dist/kuser/kinit.c	Mon Jun 21 04:17:06 2004
+++ heimdal-0.6.3/kuser/kinit.c	Thu Mar 10 13:46:41 2005
@@ -42,6 +42,8 @@
 int version_flag	= 0;
 int help_flag		= 0;
 int addrs_flag		= 1;
+int debug_flag          = 0;
+int nofork_flag         = 0;
 struct getarg_strings extra_addresses;
 int anonymous_flag	= 0;
 char *lifetime 		= NULL;
@@ -59,6 +61,20 @@
 #endif
 int fcache_version;
 
+#define PASSWD_LEN 256
+
+int got_sigchild = 0;
+int got_sigalarm = 0;
+
+void sighandler(int sig);
+void sighandler(int sig) {
+  switch(sig) {
+  case SIGALRM: got_sigalarm = 1; break;
+  case SIGCHLD: got_sigchild = 1; break;
+  }
+}
+
+
 static struct getargs args[] = {
 #ifdef KRB4
     { "524init", 	'4', arg_flag, &get_v4_tgt,
@@ -121,6 +137,12 @@
     { "anonymous",	0,   arg_flag,	&anonymous_flag,
       "request an anonymous ticket" },
 
+    { "debug",          0,   arg_flag,  &debug_flag,
+      "print some debugging information" },
+
+    { "nofork",         'n', arg_flag,  &nofork_flag,
+      "don't fork child procs" },
+
     { "version", 	0,   arg_flag, &version_flag },
     { "help",		0,   arg_flag, &help_flag }
 };
@@ -393,20 +415,25 @@
     return ret;
 }
 
+int got_passwd = 0;
+
 static krb5_error_code
 get_new_tickets(krb5_context context, 
 		krb5_principal principal,
 		krb5_ccache ccache,
-		krb5_deltat ticket_life)
+		krb5_deltat ticket_life,
+		char *passwd)
 {
     krb5_error_code ret;
     krb5_get_init_creds_opt opt;
     krb5_addresses no_addrs;
     krb5_creds cred;
-    char passwd[256];
+
     krb5_deltat start_time = 0;
     krb5_deltat renew = 0;
 
+
+
     memset(&cred, 0, sizeof(cred));
 
     krb5_get_init_creds_opt_init (&opt);
@@ -489,11 +516,13 @@
 	asprintf (&prompt, "%s's Password: ", p);
 	free (p);
 
-	if (des_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){
+	if(!got_passwd) {
+	  if (des_read_pw_string(passwd, PASSWD_LEN - 1, prompt, 0)){
 	    memset(passwd, 0, sizeof(passwd));
 	    exit(1);
+	  }
+	  got_passwd = 1;
 	}
-
 	free (prompt);
 
 	ret = krb5_get_init_creds_password (context,
@@ -519,7 +548,7 @@
 	    return exit_val;
     }
 #endif
-    memset(passwd, 0, sizeof(passwd));
+    //    memset(passwd, 0, sizeof(passwd));
 
     switch(ret){
     case 0:
@@ -574,9 +603,11 @@
     krb5_principal principal;
     int optind = 0;
     krb5_deltat ticket_life = 0;
+    char passwd[PASSWD_LEN];
 
     setprogname (argv[0]);
-    
+
+
     ret = krb5_init_context (&context);
     if (ret)
 	errx(1, "krb5_init_context failed: %d", ret);
@@ -584,6 +615,11 @@
     if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
 	usage(1);
     
+ 
+    if(lifetime && debug_flag) {
+      printf("ticket life %s\n", lifetime);
+    }
+
     if (help_flag)
 	usage (0);
 
@@ -645,13 +681,19 @@
 	    errx (1, "unparsable time: %s", lifetime);
 
 	ticket_life = tmp;
+ 
+	if(lifetime && debug_flag) {
+	  printf("ticket life %s\n", lifetime);
+	}
     }
+
 #ifdef KRB4
     if(get_v4_tgt == -1)
 	krb5_appdefault_boolean(context, "kinit", 
 				krb5_principal_get_realm(context, principal), 
 				"krb4_get_tickets", TRUE, &get_v4_tgt);
 #endif
+
     if(do_afslog == -1)
 	krb5_appdefault_boolean(context, "kinit", 
 				krb5_principal_get_realm(context, principal), 
@@ -685,7 +727,7 @@
 #ifdef KRB4
     if(!convert_524)
 #endif
-	get_new_tickets(context, principal, ccache, ticket_life);
+	get_new_tickets(context, principal, ccache, ticket_life, passwd);
 
 #ifdef KRB4
     if(get_v4_tgt)
@@ -693,9 +735,136 @@
 #endif
     if(do_afslog && k_hasafs())
 	krb5_afslog(context, ccache, NULL, NULL);
+
     if(argc > 1) {
-	ret = simple_execvp(argv[1], argv+1);
+      int pi[2];
+      int next_sleep_time = ticket_life / 2;
+
+      if(nofork_flag) {
+	memset(passwd, 0, PASSWD_LEN);
+	execvp(argv[1], argv+1);
+	exit(1);
+      }
+      else if(!fork()) {
+	execvp(argv[1], argv+1);
+	exit(1);
+      }
+
+      if (pipe(pi) == -1) {
+	krb5_warnx(context, "WARNING: running with insecure password: %s",
+		   strerror(errno));
+	pi[0] = pi[1] = -1;
+      }
+
+      while(1) {
+	int rv, status;
+	sigset_t sset;
+	signal(SIGCHLD, sighandler);
+	signal(SIGALRM, sighandler);
+
+	if (pi[1] != -1) {
+	  if (write(pi[1], passwd, PASSWD_LEN) == PASSWD_LEN)
+	    memset(passwd, 0, PASSWD_LEN);
+	  else {
+	    krb5_warnx(context,
+		       "WARNING: password cache failed, running insecurely: %s",
+		       strerror(errno));
+	    close(pi[0]);
+	    close(pi[1]);
+	    pi[0] = pi[1] = -1;
+	  }
+	}
+
+	alarm(next_sleep_time);
+
+	sigemptyset(&sset);
+	sigsuspend(&sset);
+
+	if(got_sigchild) {
+	  while(1) {
+	    rv = waitpid(-1, &status, WNOHANG);
+	    if(rv > 0) {
+	      // we only have one child
+	      if(WIFEXITED(status)) {
+		ret = WEXITSTATUS(status);
+	      }
+	      else if(WIFSIGNALED(status)) {
+		ret = 1;
+	      }
+	      goto done;
+	    }
+	    else if(rv == -1) { 
+	      if(errno == ECHILD) {
+		perror("wait() -- ECHILD");
+		ret = 1;
+		goto done;
+	      }
+	      else {
+		ret = 1;
+		perror("waitpid");
+		goto done;
+	      }
+	    }
+	    else if(rv == 0) {
+	      // WNOHANG -- no child -- don't do anything
+	      if(debug_flag) {
+		printf("wait -- 0");
+	      }
+	    }
+
+	  }
+	  got_sigchild = 0;
+	}
+
+	if(got_sigalarm) {
+	  if(debug_flag) { printf("getting new tickets... "); }
+
+	  if (pi[0] != -1) {
+	    if (read(pi[0], passwd, PASSWD_LEN) != PASSWD_LEN) {
+	      krb5_warnx(context, "WARNING: lost password, can't renew: %s",
+			 strerror(errno));
+	      pi[0] = pi[1] = -1;
+	      /* in this case we may have a partial, corrupt password */
+	      /* probably we should clear it entirely in that case */
+	      /* passwd[PASSWD_LEN - 1] = 0; */
+	      memset(passwd, 0, PASSWD_LEN);
+	    }
+	  }
+
+	  if(get_new_tickets(context, principal, ccache, ticket_life, passwd) != 0) {
+
+	    if(60 < next_sleep_time) {
+	      next_sleep_time = 60;
+	    }
+	    if(debug_flag) { printf("failed.  "); }
+	  }
+	  else {
+
+#ifdef KRB4
+	    if(get_v4_tgt)
+	      do_524init(context, ccache, NULL, server);
+	    if(do_afslog && k_hasafs())
+	      krb5_afslog(context, ccache, NULL, NULL);
+#endif
+ 
+	    if(debug_flag) { printf("succeeded.  "); }
+	    next_sleep_time = ticket_life / 2;
+	  }
+	  if(debug_flag) { 
+	    printf("Will renew again in %d\n", next_sleep_time); 
+	  }
+	  got_sigalarm = 0;
+	}
+	
+      } // sleep/ticket renew loop
+       
+    done:
 	krb5_cc_destroy(context, ccache);
+	memset(passwd, 0, PASSWD_LEN);  
+	if (pi[0] != -1) {
+	  close(pi[0]);
+	  close(pi[1]);
+	}
 #ifdef KRB4
 	dest_tkt();
 #endif
diff -ur heimdal-0.6.4/kuser/kuser_locl.h sup.heimdal.backup/0.6.4/src/heimdal-0.6.4/kuser/kuser_locl.h
--- heimdal-0.6.4/kuser/kuser_locl.h	Mon Apr 18 16:52:20 2005
+++ sup.heimdal.backup/0.6.4/src/heimdal-0.6.4/kuser/kuser_locl.h	Thu Apr 28 09:55:30 2005
@@ -84,6 +84,9 @@
 #ifdef HAVE_SYS_IOCCOM_H
 #include <sys/ioccom.h>
 #endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
 #include <kafs.h>
 #include "crypto-headers.h" /* for des_read_pw_string */
 
diff -ur heimdal-0.6.4-dist/appl/su/su.c heimdal-0.6.4/appl/su/su.c
--- heimdal-0.6.4-dist/appl/su/su.c	Mon Apr 18 16:52:11 2005
+++ heimdal-0.6.4/appl/su/su.c	Thu Apr 28 09:54:27 2005
@@ -70,6 +70,12 @@
 #define _PATH_BSHELL "/bin/sh"
 #endif
 
+#ifdef KRB5
+#ifdef KRB4
+static krb5_error_code krb5_to4 (krb5_ccache);
+#endif
+#endif
+
 int kerberos_flag = 1;
 int csh_f_flag;
 int full_login;
@@ -252,8 +258,11 @@
     /* we want to export this even if we don't directly support KRB4 */
     set_tkfile();
     esetenv("KRBTKFILE", tkfile, 1);
-            
-    /* convert creds? */
+
+#ifdef KRB4
+    krb5_to4(ccache2);
+#endif
+
     if(k_hasafs()) {
 	if (k_setpag() == 0)
 	    krb5_afslog(context, ccache2, NULL, NULL);
@@ -263,6 +272,65 @@
     krb5_cc_destroy(context, ccache);
     return 0;
 }
+
+#ifdef KRB4
+static krb5_error_code
+krb5_to4 (krb5_ccache id)
+{
+    krb5_error_code ret;
+    krb5_principal princ;
+
+    int get_v4_tgt;
+
+    ret = krb5_cc_get_principal(context, id, &princ);
+    if(ret == 0) {
+	krb5_appdefault_boolean(context, "su", 
+				krb5_principal_get_realm(context, princ), 
+				"krb4_get_tickets", FALSE, &get_v4_tgt);
+	krb5_free_principal(context, princ);
+    } else {
+	krb5_realm realm = NULL;
+	krb5_get_default_realm(context, &realm);
+	krb5_appdefault_boolean(context, "su", 
+				realm, 
+				"krb4_get_tickets", FALSE, &get_v4_tgt);
+	free(realm);
+    }
+
+    if (get_v4_tgt) {
+        CREDENTIALS c;
+        krb5_creds mcred, cred;
+	krb5_error_code ret;
+	krb5_principal princ;
+
+	ret = krb5_cc_get_principal (context, id, &princ);
+	if (ret)
+	    return ret;
+
+	ret = krb5_make_principal(context, &mcred.server,
+				  princ->realm,
+				  "krbtgt",
+				  princ->realm,
+				  NULL);
+	krb5_free_principal (context, princ);
+	if (ret)
+	    return ret;
+
+	ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
+	if(ret == 0) {
+	    ret = krb524_convert_creds_kdc_ccache(context, id, &cred, &c);
+	    if(ret == 0) {
+		tf_setup(&c, c.pname, c.pinst);
+	    }
+	    memset(&c, 0, sizeof(c));
+	    krb5_free_creds_contents(context, &cred);
+	}
+	krb5_free_principal(context, mcred.server);
+    }
+    return 0;
+}
+#endif /* KRB4 */
+
 #endif
 
 #ifdef KRB4