#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:
+ */