gpio: Fixed usage message.
[utils.git] / krb5-agent.c
index 6cbb783..a24423e 100644 (file)
@@ -1,9 +1,289 @@
+/*
+ *  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 <krb5.h>
+#include <time.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+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;
+
+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)
+{
+    switch(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(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)
 {
-    return(0);
+    char *p;
+    int c;
+    time_t interval, now;
+    pid_t wpid;
+    int ret, status;
+    
+    interval = -1;
+    while((c = getopt(argc, argv, "+hi:vqf")) != -1) {
+       switch(c) {
+       case 'v':
+           verbose++;
+           break;
+       case 'q':
+           quiet = 1;
+           break;
+       case 'f':
+           failsafe = 1;
+           break;
+       case 'i':
+           p = optarg + strlen(optarg) - 1;
+           if((*p >= 'a') && (*p <= 'z')) {
+               if(*p == 'm')
+                   interval = 60;
+               else if(*p == 'h')
+                   interval = 3600;
+               else if(*p == 'd')
+                   interval = 86400;
+               else
+                   interval = 1;
+               *p = 0;
+           } else {
+               interval = 1;
+           }
+           interval *= atoi(optarg);
+           break;
+       case 'h':
+       case '?':
+       case ':':
+       default:
+           fprintf(stderr, "usage: krb5-agent [-hvqf] [-i interval] [program args...]\n");
+           exit((c == 'h')?0:1);
+       }
+    }
+    
+    atexit(cleanup_krb5);
+    if((ret = krb5_init_context(&context)) != 0) {
+       if(!quiet)
+           fprintf(stderr, "could not initialize Kerberos context: %s\n", error_message(ret));
+       if(!failsafe)
+           exit(1);
+    }
+    if(context != NULL) {
+       if((ret = krb5_cc_default(context, &ccache)) != 0) {
+           if(!quiet)
+               fprintf(stderr, "could not initialize Kerberos context: %s\n", error_message(ret));
+           if(!failsafe)
+               exit(1);
+       }
+    }
+    
+    if(optind < argc) {
+       execmode = 1;
+       signal(SIGCHLD, sighandler);
+       if((child = fork()) < 0) {
+           perror("fork");
+           exit(1);
+       }
+       if(child == 0) {
+           char buf[80];
+           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 = time(NULL);
+    if(interval >= 0)
+       renewat = now + interval;
+    while(1) {
+       if(died) {
+           wpid = waitpid(-1, &status, WNOHANG);
+           if(wpid < 0) {
+               perror("waitpid");
+           } else if(execmode && (wpid == child)) {
+               /* Try to preserve exit status as best as we can... */
+               if(WIFEXITED(status)) {
+                   exit(WEXITSTATUS(status));
+               } else {
+                   cleanup_krb5();
+                   signal(WTERMSIG(status), SIG_DFL);
+                   kill(getpid(), WTERMSIG(status));
+                   exit(255);
+               }
+           }
+           died = 0;
+       }
+       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((renewat >= 0) && (now >= renewat)) {
+           renew();
+           now = time(NULL);
+           if(interval >= 0)
+               renewat = now + interval;
+       }
+    }
 }
+
+/*
+ * Local Variables:
+ * compile-command: "gcc -Wall -g -o krb5-agent krb5-agent.c -lkrb5"
+ * End:
+ */