1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 2010-2021 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
log_krb5_errmsg(krb5_context ctx,const char * func,krb5_error_code rc)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
kinit_check_tgt(kinit_data * kid,int * remaining)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*
kinit_qtask(void * ctx,void * arg)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
kinit_initialize(void)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
init_module(int argc,char * argv[])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
term_module()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
296