]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/kinit/kinit.c
Happy New Year
[openldap] / contrib / slapd-modules / kinit / kinit.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2010-2015 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15
16 #include <portable.h>
17
18 #ifndef SLAPD_MOD_KINIT
19 #define SLAPD_MOD_KINIT SLAPD_MOD_DYNAMIC
20 #endif
21
22 #ifdef SLAPD_MOD_KINIT
23
24 #include <slap.h>
25 #include "ldap_rq.h"
26 #include <ac/errno.h>
27 #include <ac/string.h>
28 #include <krb5/krb5.h>
29
30 typedef struct kinit_data {
31         krb5_context ctx;
32         krb5_ccache ccache;
33         krb5_keytab keytab;
34         krb5_principal princ;
35         krb5_get_init_creds_opt *opts;
36 } kinit_data;
37
38 static char* principal;
39 static char* kt_name;
40 static kinit_data *kid;
41
42 static void
43 log_krb5_errmsg( krb5_context ctx, const char* func, krb5_error_code rc )
44 {
45         const char* errmsg = krb5_get_error_message(ctx, rc);
46         Log2(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, "slapd-kinit: %s: %s\n", func, errmsg);
47         krb5_free_error_message(ctx, errmsg);
48         return;
49 }
50
51 static int
52 kinit_check_tgt(kinit_data *kid, int *remaining)
53 {
54         int ret=3;
55         krb5_principal princ;
56         krb5_error_code rc;
57         krb5_cc_cursor cursor;
58         krb5_creds creds;
59         char *name;
60         time_t now=time(NULL);
61
62         rc = krb5_cc_get_principal(kid->ctx, kid->ccache, &princ);
63         if (rc) {
64                 log_krb5_errmsg(kid->ctx, "krb5_cc_get_principal", rc);
65                 return 2;
66         } else {
67                 if (!krb5_principal_compare(kid->ctx, kid->princ, princ)) {
68                         Log0(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
69                                         "Principal in ccache does not match requested principal\n");
70                         krb5_free_principal(kid->ctx, princ);
71                         return 2;
72                 }
73         }
74
75         rc = krb5_cc_start_seq_get(kid->ctx, kid->ccache, &cursor);
76         if (rc) {
77                 log_krb5_errmsg(kid->ctx, "krb5_cc_start_seq_get", rc);
78                 krb5_free_principal(kid->ctx, princ);
79                 return -1;
80         }
81
82         while (!(rc = krb5_cc_next_cred(kid->ctx, kid->ccache, &cursor, &creds))) {
83                 if (krb5_is_config_principal(kid->ctx, creds.server)) {
84                         krb5_free_cred_contents(kid->ctx, &creds);
85                         continue;
86                 }
87
88                 if (creds.server->length==2 &&
89                                 (!strcmp(creds.server->data[0].data, "krbtgt")) &&
90                                 (!strcmp(creds.server->data[1].data, princ->realm.data))) {
91
92                         krb5_unparse_name(kid->ctx, creds.server, &name);
93
94                         *remaining = (time_t)creds.times.endtime-now;
95                         if ( *remaining <= 0) {
96                                 Log1(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
97                                                 "kinit_qtask: TGT (%s) expired\n", name);
98                         } else {
99                                 Log4(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
100                                                 "kinit_qtask: TGT (%s) expires in %dh:%02dm:%02ds\n",
101                                                 name, *remaining/3600, (*remaining%3600)/60, *remaining%60);
102                         }
103                         free(name);
104
105                         if (*remaining <= 30) {
106                                 if (creds.times.renew_till-60 > now) {
107                                         int renewal=creds.times.renew_till-now;
108                                         Log3(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
109                                                         "kinit_qtask: Time remaining for renewal: %dh:%02dm:%02ds\n",
110                                                         renewal/3600, (renewal%3600)/60,  renewal%60);
111                                         ret = 1;
112                                 } else {
113                                         Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
114                                                         "kinit_qtask: Only short time left for renewal. "
115                                                         "Trying to re-init.\n");
116                                         ret = 2;
117                                 }
118                         } else {
119                                 ret=0;
120                         }
121                         krb5_free_cred_contents(kid->ctx, &creds);
122                         break;
123                 }
124                 krb5_free_cred_contents(kid->ctx, &creds);
125
126         }
127         krb5_cc_end_seq_get(kid->ctx, kid->ccache, &cursor);
128         krb5_free_principal(kid->ctx, princ);
129         return ret;
130 }
131
132 void*
133 kinit_qtask( void *ctx, void *arg )
134 {
135         struct re_s     *rtask = arg;
136         kinit_data      *kid = (kinit_data*)rtask->arg;
137         krb5_error_code rc;
138         krb5_creds creds;
139         int nextcheck, remaining, renew=0;
140         Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_qtask: running TGT check\n");
141
142         memset(&creds, 0, sizeof(creds));
143
144         renew = kinit_check_tgt(kid, &remaining);
145
146         if (renew > 0) {
147                 if (renew==1) {
148                         Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
149                                         "kinit_qtask: Trying to renew TGT: ");
150                         rc = krb5_get_renewed_creds(kid->ctx, &creds, kid->princ, kid->ccache, NULL);
151                         if (rc!=0) {
152                                 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n");
153                                 log_krb5_errmsg( kid->ctx,
154                                                 "kinit_qtask, Renewal failed: krb5_get_renewed_creds", rc );
155                                 renew++;
156                         } else {
157                                 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n");
158                                 krb5_cc_initialize(kid->ctx, kid->ccache, creds.client);
159                                 krb5_cc_store_cred(kid->ctx, kid->ccache, &creds);
160                                 krb5_free_cred_contents(kid->ctx, &creds);
161                                 renew=kinit_check_tgt(kid, &remaining);
162                         }
163                 }
164                 if (renew > 1) {
165                         Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
166                                         "kinit_qtask: Trying to get new TGT: ");
167                         rc = krb5_get_init_creds_keytab( kid->ctx, &creds, kid->princ,
168                                         kid->keytab, 0, NULL, kid->opts);
169                         if (rc) {
170                                 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n");
171                                 log_krb5_errmsg(kid->ctx, "krb5_get_init_creds_keytab", rc);
172                         } else {
173                                 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n");
174                                 renew=kinit_check_tgt(kid, &remaining);
175                         }
176                         krb5_free_cred_contents(kid->ctx, &creds);
177                 }
178         }
179         if (renew == 0) {
180                 nextcheck = remaining-30;
181         } else {
182                 nextcheck = 60;
183         }
184
185         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
186         if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
187                 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
188         }
189         Log3(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
190                         "kinit_qtask: Next TGT check in %dh:%02dm:%02ds\n",
191                         nextcheck/3600, (nextcheck%3600)/60,  nextcheck%60);
192         rtask->interval.tv_sec = nextcheck;
193         ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
194         slap_wake_listener();
195         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
196         return NULL;
197 }
198
199 int
200 kinit_initialize(void)
201 {
202         Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_initialize\n" );
203         krb5_error_code rc;
204         struct re_s *task = NULL;
205
206         kid = ch_calloc(1, sizeof(kinit_data) );
207
208         rc = krb5_init_context( &kid->ctx );
209         if ( !rc )
210                 rc = krb5_cc_default(kid->ctx, &kid->ccache );
211
212         if ( !rc ) {
213                 if (!principal) {
214                         int len=STRLENOF("ldap/")+global_host_bv.bv_len+1;
215                         principal=ch_calloc(len, 1);
216                         snprintf(principal, len, "ldap/%s", global_host_bv.bv_val);
217                         Log1(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Principal <%s>\n", principal);
218
219                 }
220                 rc = krb5_parse_name(kid->ctx, principal, &kid->princ);
221         }
222
223         if ( !rc && kt_name) {
224                 rc = krb5_kt_resolve(kid->ctx, kt_name, &kid->keytab);
225         }
226
227         if ( !rc )
228                 rc = krb5_get_init_creds_opt_alloc(kid->ctx, &kid->opts);
229
230         if ( !rc )
231                 rc = krb5_get_init_creds_opt_set_out_ccache( kid->ctx, kid->opts, kid->ccache);
232
233         if ( !rc ) {
234                 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
235                 task = ldap_pvt_runqueue_insert( &slapd_rq, 10, kinit_qtask, (void*)kid,
236                                 "kinit_qtask", "ldap/bronsted.g17.lan@G17.LAN" );
237                 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
238         }
239
240         if (rc) {
241                 log_krb5_errmsg(kid->ctx, "kinit_initialize", rc);
242                 rc = -1;
243         }
244         return rc;
245 }
246
247 #if SLAPD_MOD_KINIT == SLAPD_MOD_DYNAMIC
248 int init_module(int argc, char *argv[]) {
249         if (argc > 0) {
250                 principal = ch_strdup(argv[0]);
251         }
252         if (argc > 1) {
253                 kt_name = ch_strdup(argv[1]);
254         }
255         if (argc > 2) {
256                 return -1;
257         }
258         return kinit_initialize();
259 }
260
261 int
262 term_module() {
263         if (principal)
264                 ch_free(principal);
265         if (kt_name)
266                 ch_free(kt_name);
267         if (kid) {
268                 struct re_s *task;
269
270                 task=ldap_pvt_runqueue_find( &slapd_rq, kinit_qtask, (void*)kid);
271                 if (task) {
272                         if ( ldap_pvt_runqueue_isrunning(&slapd_rq, task) ) {
273                                 ldap_pvt_runqueue_stoptask(&slapd_rq, task);
274                         }
275                         ldap_pvt_runqueue_remove(&slapd_rq, task);
276                 }
277                 if ( kid->ctx ) {
278                         if ( kid->princ )
279                                 krb5_free_principal(kid->ctx, kid->princ);
280                         if ( kid->ccache )
281                                 krb5_cc_close(kid->ctx, kid->ccache);
282                         if ( kid->keytab )
283                                 krb5_kt_close(kid->ctx, kid->keytab);
284                         if ( kid->opts )
285                                 krb5_get_init_creds_opt_free(kid->ctx, kid->opts);
286                         krb5_free_context(kid->ctx);
287                 }
288                 ch_free(kid);
289         }
290         return 0;
291 }
292 #endif
293
294 #endif /* SLAPD_MOD_KINIT */
295