First should-be-working version.
[utils.git] / krb5-agent.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <krb5.h>
5 #include <time.h>
6 #include <string.h>
7 #include <signal.h>
8 #include <sys/wait.h>
9
10 volatile int died = 0;
11 pid_t child;
12 int execmode = 0, failsafe = 0;
13 int verbose = 0, quiet = 0;
14 time_t renewat;
15
16 krb5_context context = NULL;
17 krb5_ccache ccache = NULL;
18
19 void cleanup_krb5(void)
20 {
21     if(ccache != NULL)
22         krb5_cc_close(context, ccache);
23     ccache = NULL;
24     if(context != NULL)
25         krb5_free_context(context);
26     context = NULL;
27 }
28
29 void sighandler(int sig)
30 {
31     switch(sig) {
32     case SIGCHLD:
33         died = 1;
34         break;
35     case SIGHUP:
36     case SIGINT:
37     case SIGTERM:
38         if(execmode)
39             kill(child, sig);
40         break;
41     }
42 }
43
44 void renew(void)
45 {
46     int ret;
47     krb5_principal me;
48     krb5_creds creds;
49     
50     if(context == NULL)
51         return;
52     if(ccache == NULL) {
53         if((ret = krb5_cc_default(context, &ccache)) != 0) {
54             if(!quiet)
55                 fprintf(stderr, "could not initialize Kerberos context: %s\n", error_message(ret));
56             return;
57         }
58     }
59     
60     me = NULL;
61     memset(&creds, 0, sizeof(creds));
62     if((ret = krb5_cc_get_principal(context, ccache, &me)) != 0) {
63         if(!quiet)
64             fprintf(stderr, "could not get principal from cache: %s\n", error_message(ret));
65         goto out;
66     }
67     if((ret = krb5_get_renewed_creds(context, &creds, me, ccache, NULL)) != 0) {
68         if(!quiet)
69             fprintf(stderr, "could not get renewed credentials: %s\n", error_message(ret));
70         goto out;
71     }
72     if((ret = krb5_cc_initialize(context, ccache, me)) != 0) {
73         if(!quiet)
74             fprintf(stderr, "could not reinitialize cache: %s\n", error_message(ret));
75         krb5_free_cred_contents(context, &creds);
76         goto out;
77     }
78     if((ret = krb5_cc_store_cred(context, ccache, &creds)) != 0) {
79         if(!quiet)
80             fprintf(stderr, "could not store renewed credentials: %s\n", error_message(ret));
81         krb5_free_cred_contents(context, &creds);
82         goto out;
83     }
84     krb5_free_cred_contents(context, &creds);
85     if(verbose >= 1)
86         printf("successfully renewed credentials\n");
87     
88  out:
89     if(me != NULL)
90         krb5_free_principal(context, me);
91 }
92
93 time_t goodrenewtime(void)
94 {
95     int ret;
96     krb5_principal me;
97     krb5_cc_cursor cur;
98     krb5_creds creds;
99     time_t now, good;
100     
101     if(context == NULL)
102         return(-1);
103     if(ccache == NULL) {
104         if((ret = krb5_cc_default(context, &ccache)) != 0) {
105             if(!quiet)
106                 fprintf(stderr, "could not initialize Kerberos context: %s\n", error_message(ret));
107             return(-1);
108         }
109     }
110     
111     me = NULL;
112     cur = NULL;
113     good = -1;
114     if((ret = krb5_cc_get_principal(context, ccache, &me)) != 0) {
115         if(!quiet)
116             fprintf(stderr, "could not get principal from cache: %s\n", error_message(ret));
117         goto out;
118     }
119     if((ret = krb5_cc_start_seq_get(context, ccache, &cur)) != 0) {
120         if(!quiet)
121             fprintf(stderr, "could not open credentials cache: %s\n", error_message(ret));
122         goto out;
123     }
124     now = time(NULL);
125     while(!krb5_cc_next_cred(context, ccache, &cur, &creds)) {
126         if(!strcmp(krb5_princ_component(context, creds.server, 0)->data, KRB5_TGS_NAME) &&
127            !strcmp(krb5_princ_component(context, creds.server, 1)->data, me->realm.data)) {
128             if(!creds.times.starttime)
129                 creds.times.starttime = creds.times.authtime;
130             good = (creds.times.starttime + (((creds.times.endtime - creds.times.starttime) * 9) / 10));
131             break;
132         }
133         krb5_free_cred_contents(context, &creds);
134     }
135     
136  out:
137     if(cur != NULL)
138         krb5_cc_end_seq_get(context, ccache, &cur);
139     if(me != NULL)
140         krb5_free_principal(context, me);
141     return(good);
142 }
143
144 int main(int argc, char **argv)
145 {
146     char *p;
147     int c;
148     time_t interval, now;
149     pid_t wpid;
150     int ret, status;
151     
152     interval = -1;
153     while((c = getopt(argc, argv, "+hi:vqf")) != -1) {
154         switch(c) {
155         case 'v':
156             verbose++;
157             break;
158         case 'q':
159             quiet = 1;
160             break;
161         case 'f':
162             failsafe = 1;
163             break;
164         case 'i':
165             p = optarg + strlen(optarg) - 1;
166             if((*p >= 'a') && (*p <= 'z')) {
167                 if(*p == 'm')
168                     interval = 60;
169                 else if(*p == 'h')
170                     interval = 3600;
171                 else if(*p == 'd')
172                     interval = 86400;
173                 else
174                     interval = 1;
175                 *p = 0;
176             } else {
177                 interval = 1;
178             }
179             interval *= atoi(optarg);
180             break;
181         case 'h':
182         case '?':
183         case ':':
184         default:
185             fprintf(stderr, "usage: krb5-agent [-hvqf] [-i interval] [program args...]\n");
186             exit((c == 'h')?0:1);
187         }
188     }
189     
190     atexit(cleanup_krb5);
191     if((ret = krb5_init_context(&context)) != 0) {
192         if(!quiet)
193             fprintf(stderr, "could not initialize Kerberos context: %s\n", error_message(ret));
194         if(!failsafe)
195             exit(1);
196     }
197     if(context != NULL) {
198         if((ret = krb5_cc_default(context, &ccache)) != 0) {
199             if(!quiet)
200                 fprintf(stderr, "could not initialize Kerberos context: %s\n", error_message(ret));
201             if(!failsafe)
202                 exit(1);
203         }
204     }
205     
206     if(optind < argc) {
207         execmode = 1;
208         signal(SIGCHLD, sighandler);
209         if((child = fork()) < 0) {
210             perror("fork");
211             exit(1);
212         }
213         if(child == 0) {
214             char buf[80];
215             snprintf(buf, 80, "KRB5_AGENT_PID=%i", getpid());
216             putenv(buf);
217             execvp(argv[optind], argv + optind);
218             perror(argv[optind]);
219             exit(255);
220         }
221         signal(SIGHUP, sighandler);
222         signal(SIGINT, sighandler);
223         signal(SIGTERM, sighandler);
224     }
225     now = time(NULL);
226     if(interval >= 0)
227         renewat = now + interval;
228     while(1) {
229         if(died) {
230             wpid = waitpid(-1, &status, WNOHANG);
231             if(wpid < 0) {
232                 perror("waitpid");
233             } else if(execmode && (wpid == child)) {
234                 /* Try to preserve exit status as best as we can... */
235                 if(WIFEXITED(status)) {
236                     exit(WEXITSTATUS(status));
237                 } else {
238                     cleanup_krb5();
239                     signal(WTERMSIG(status), SIG_DFL);
240                     kill(getpid(), WTERMSIG(status));
241                     exit(255);
242                 }
243             }
244             died = 0;
245         }
246         if(interval < 0) {
247             if((renewat = goodrenewtime()) < 0) {
248                 renewat = -1;
249             } else if(verbose >= 2) {
250                 printf("will renew tickets at %s", ctime(&renewat));
251             }
252         }
253         if(renewat < 0)
254             sleep(60);
255         else
256             sleep(renewat - now);
257         now = time(NULL);
258         if((renewat >= 0) && (now >= renewat)) {
259             renew();
260             now = time(NULL);
261             if(interval >= 0)
262                 renewat = now + interval;
263         }
264     }
265 }
266
267 /*
268  * Local Variables:
269  * compile-command: "gcc -Wall -g -o krb5-agent krb5-agent.c -lkrb5"
270  * End:
271  */