First should-be-working version.
[utils.git] / krb5-agent.c
CommitLineData
d5996a1f
DC
1#include <stdlib.h>
2#include <stdio.h>
3#include <unistd.h>
4#include <krb5.h>
e73ae134
DC
5#include <time.h>
6#include <string.h>
7#include <signal.h>
8#include <sys/wait.h>
9
e73ae134
DC
10volatile int died = 0;
11pid_t child;
12int execmode = 0, failsafe = 0;
13int verbose = 0, quiet = 0;
116e305b
DC
14time_t renewat;
15
e73ae134
DC
16krb5_context context = NULL;
17krb5_ccache ccache = NULL;
18
19void cleanup_krb5(void)
20{
21 if(ccache != NULL)
22 krb5_cc_close(context, ccache);
116e305b 23 ccache = NULL;
e73ae134
DC
24 if(context != NULL)
25 krb5_free_context(context);
116e305b 26 context = NULL;
e73ae134
DC
27}
28
29void sighandler(int sig)
30{
31 switch(sig) {
32 case SIGCHLD:
33 died = 1;
34 break;
116e305b
DC
35 case SIGHUP:
36 case SIGINT:
37 case SIGTERM:
38 if(execmode)
39 kill(child, sig);
40 break;
e73ae134
DC
41 }
42}
43
44void renew(void)
45{
46 int ret;
116e305b
DC
47 krb5_principal me;
48 krb5_creds creds;
e73ae134 49
116e305b 50 if(context == NULL)
e73ae134 51 return;
116e305b
DC
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
93time_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);
e73ae134 142}
d5996a1f
DC
143
144int main(int argc, char **argv)
145{
e73ae134
DC
146 char *p;
147 int c;
116e305b 148 time_t interval, now;
e73ae134
DC
149 pid_t wpid;
150 int ret, status;
151
116e305b 152 interval = -1;
e73ae134
DC
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;
116e305b 166 if((*p >= 'a') && (*p <= 'z')) {
e73ae134
DC
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 }
116e305b
DC
221 signal(SIGHUP, sighandler);
222 signal(SIGINT, sighandler);
223 signal(SIGTERM, sighandler);
e73ae134 224 }
116e305b
DC
225 now = time(NULL);
226 if(interval >= 0)
227 renewat = now + interval;
e73ae134
DC
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 {
116e305b 238 cleanup_krb5();
e73ae134
DC
239 signal(WTERMSIG(status), SIG_DFL);
240 kill(getpid(), WTERMSIG(status));
241 exit(255);
242 }
243 }
244 died = 0;
245 }
116e305b
DC
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);
e73ae134 257 now = time(NULL);
116e305b 258 if((renewat >= 0) && (now >= renewat)) {
e73ae134 259 renew();
116e305b
DC
260 now = time(NULL);
261 if(interval >= 0)
262 renewat = now + interval;
263 }
e73ae134 264 }
d5996a1f 265}
e73ae134
DC
266
267/*
268 * Local Variables:
269 * compile-command: "gcc -Wall -g -o krb5-agent krb5-agent.c -lkrb5"
270 * End:
271 */