xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/kinit/kinit.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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