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