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