anndl: Replaced with new Python version.
[utils.git] / krb5-agent.c
index 7edd369..a24423e 100644 (file)
@@ -1,3 +1,21 @@
+/*
+ *  krb5-agent - Renews Kerberos tickets periodically.
+ *  Copyright (C) 2004 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 <unistd.h>
 #include <signal.h>
 #include <sys/wait.h>
 
-#define STD_INTERVAL (3600 * 8)
-
 volatile int died = 0;
 pid_t child;
 int execmode = 0, failsafe = 0;
 int verbose = 0, quiet = 0;
+time_t renewat;
+
 krb5_context context = NULL;
 krb5_ccache ccache = NULL;
 
@@ -20,8 +38,10 @@ void cleanup_krb5(void)
 {
     if(ccache != NULL)
        krb5_cc_close(context, ccache);
+    ccache = NULL;
     if(context != NULL)
        krb5_free_context(context);
+    context = NULL;
 }
 
 void sighandler(int sig)
@@ -30,26 +50,124 @@ void sighandler(int sig)
     case SIGCHLD:
        died = 1;
        break;
+    case SIGHUP:
+    case SIGINT:
+    case SIGTERM:
+       if(execmode)
+           kill(child, sig);
+       break;
     }
 }
 
 void renew(void)
 {
     int ret;
+    krb5_principal me;
+    krb5_creds creds;
     
-    if(ccache == NULL)
+    if(context == NULL)
        return;
+    if(ccache == NULL) {
+       if((ret = krb5_cc_default(context, &ccache)) != 0) {
+           if(!quiet)
+               fprintf(stderr, "could not initialize Kerberos context: %s\n", error_message(ret));
+           return;
+       }
+    }
+    
+    me = NULL;
+    memset(&creds, 0, sizeof(creds));
+    if((ret = krb5_cc_get_principal(context, ccache, &me)) != 0) {
+       if(!quiet)
+           fprintf(stderr, "could not get principal from cache: %s\n", error_message(ret));
+       goto out;
+    }
+    if((ret = krb5_get_renewed_creds(context, &creds, me, ccache, NULL)) != 0) {
+       if(!quiet)
+           fprintf(stderr, "could not get renewed credentials: %s\n", error_message(ret));
+       goto out;
+    }
+    if((ret = krb5_cc_initialize(context, ccache, me)) != 0) {
+       if(!quiet)
+           fprintf(stderr, "could not reinitialize cache: %s\n", error_message(ret));
+       krb5_free_cred_contents(context, &creds);
+       goto out;
+    }
+    if((ret = krb5_cc_store_cred(context, ccache, &creds)) != 0) {
+       if(!quiet)
+           fprintf(stderr, "could not store renewed credentials: %s\n", error_message(ret));
+       krb5_free_cred_contents(context, &creds);
+       goto out;
+    }
+    krb5_free_cred_contents(context, &creds);
+    if(verbose >= 1)
+       printf("successfully renewed credentials\n");
+    
+ out:
+    if(me != NULL)
+       krb5_free_principal(context, me);
+}
+
+time_t goodrenewtime(void)
+{
+    int ret;
+    krb5_principal me;
+    krb5_cc_cursor cur;
+    krb5_creds creds;
+    time_t now, good;
+    
+    if(context == NULL)
+       return(-1);
+    if(ccache == NULL) {
+       if((ret = krb5_cc_default(context, &ccache)) != 0) {
+           if(!quiet)
+               fprintf(stderr, "could not initialize Kerberos context: %s\n", error_message(ret));
+           return(-1);
+       }
+    }
+    
+    me = NULL;
+    cur = NULL;
+    good = -1;
+    if((ret = krb5_cc_get_principal(context, ccache, &me)) != 0) {
+       if(!quiet)
+           fprintf(stderr, "could not get principal from cache: %s\n", error_message(ret));
+       goto out;
+    }
+    if((ret = krb5_cc_start_seq_get(context, ccache, &cur)) != 0) {
+       if(!quiet)
+           fprintf(stderr, "could not open credentials cache: %s\n", error_message(ret));
+       goto out;
+    }
+    now = time(NULL);
+    while(!krb5_cc_next_cred(context, ccache, &cur, &creds)) {
+       if(!strcmp(krb5_princ_component(context, creds.server, 0)->data, KRB5_TGS_NAME) &&
+          !strcmp(krb5_princ_component(context, creds.server, 1)->data, me->realm.data)) {
+           if(!creds.times.starttime)
+               creds.times.starttime = creds.times.authtime;
+           good = (creds.times.starttime + (((creds.times.endtime - creds.times.starttime) * 9) / 10));
+           break;
+       }
+       krb5_free_cred_contents(context, &creds);
+    }
+    
+ out:
+    if(cur != NULL)
+       krb5_cc_end_seq_get(context, ccache, &cur);
+    if(me != NULL)
+       krb5_free_principal(context, me);
+    return(good);
 }
 
 int main(int argc, char **argv)
 {
     char *p;
     int c;
-    time_t interval, last, now;
+    time_t interval, now;
     pid_t wpid;
     int ret, status;
     
-    interval = STD_INTERVAL;
+    interval = -1;
     while((c = getopt(argc, argv, "+hi:vqf")) != -1) {
        switch(c) {
        case 'v':
@@ -63,7 +181,7 @@ int main(int argc, char **argv)
            break;
        case 'i':
            p = optarg + strlen(optarg) - 1;
-           if((*p >= 'a') || (*p <= 'z')) {
+           if((*p >= 'a') && (*p <= 'z')) {
                if(*p == 'm')
                    interval = 60;
                else if(*p == 'h')
@@ -112,14 +230,19 @@ int main(int argc, char **argv)
        }
        if(child == 0) {
            char buf[80];
-           snprintf(buf, 80, "KRB5_AGENT_PID=%i", getpid());
+           snprintf(buf, 80, "KRB5_AGENT_PID=%i", getppid());
            putenv(buf);
            execvp(argv[optind], argv + optind);
            perror(argv[optind]);
            exit(255);
        }
+       signal(SIGHUP, sighandler);
+       signal(SIGINT, sighandler);
+       signal(SIGTERM, sighandler);
     }
-    now = last = time(NULL);
+    now = time(NULL);
+    if(interval >= 0)
+       renewat = now + interval;
     while(1) {
        if(died) {
            wpid = waitpid(-1, &status, WNOHANG);
@@ -130,6 +253,7 @@ int main(int argc, char **argv)
                if(WIFEXITED(status)) {
                    exit(WEXITSTATUS(status));
                } else {
+                   cleanup_krb5();
                    signal(WTERMSIG(status), SIG_DFL);
                    kill(getpid(), WTERMSIG(status));
                    exit(255);
@@ -137,10 +261,24 @@ int main(int argc, char **argv)
            }
            died = 0;
        }
-       sleep((last + interval) - now);
+       if(interval < 0) {
+           if((renewat = goodrenewtime()) < 0) {
+               renewat = -1;
+           } else if(verbose >= 2) {
+               printf("will renew tickets at %s", ctime(&renewat));
+           }
+       }
+       if(renewat < 0)
+           sleep(60);
+       else
+           sleep(renewat - now);
        now = time(NULL);
-       if(now >= last + interval)
+       if((renewat >= 0) && (now >= renewat)) {
            renew();
+           now = time(NULL);
+           if(interval >= 0)
+               renewat = now + interval;
+       }
     }
 }