]>
Commit | Line | Data |
---|---|---|
743d6937 TM |
1 | /* ldap_krb_helper.c |
2 | ||
3 | Helper routings for allowing LDAP to read configuration with GSSAPI/krb auth */ | |
4 | ||
5 | /* | |
7512d88b | 6 | * Copyright (c) 2015-2017 by Internet Systems Consortium, Inc. ("ISC") |
743d6937 TM |
7 | * Copyright (c) 2014 William B. |
8 | * All rights reserved. | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * | |
14 | * 1. Redistributions of source code must retain the above copyright | |
15 | * notice, this list of conditions and the following disclaimer. | |
16 | * 2. Redistributions in binary form must reproduce the above copyright | |
17 | * notice, this list of conditions and the following disclaimer in the | |
18 | * documentation and/or other materials provided with the distribution. | |
19 | * 3. Neither the name of The Internet Software Consortium nor the names | |
20 | * of its contributors may be used to endorse or promote products derived | |
21 | * from this software without specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND | |
24 | * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
25 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
27 | * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR | |
28 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
29 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
30 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
31 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
32 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
33 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
34 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
35 | * SUCH DAMAGE. | |
36 | * | |
37 | * This helper was written by William Brown <william@adelaide.edu.au>, | |
38 | * inspired by krb5_helper.c from bind-dyndb-ldap by Simo Sorce (Redhat) | |
39 | */ | |
40 | #if defined(LDAP_USE_GSSAPI) | |
41 | ||
42 | #include "dhcpd.h" | |
43 | #include "ldap_krb_helper.h" | |
44 | ||
45 | #include <string.h> | |
46 | #include <stdio.h> | |
47 | #include <unistd.h> | |
48 | #include <time.h> | |
49 | ||
50 | #define KRB_DEFAULT_KEYTAB "FILE:/etc/dhcp/dhcp.keytab" | |
51 | #define KRB_MIN_TIME 300 | |
52 | ||
53 | #define CHECK_KRB5(ctx, err, msg, ...) \ | |
54 | do { \ | |
55 | if (err) { \ | |
56 | const char * errmsg = krb5_get_error_message(ctx, err); \ | |
57 | log_error("Err: %s -> %s\n", msg, errmsg); \ | |
58 | result = ISC_R_FAILURE; \ | |
59 | goto cleanup; \ | |
60 | } \ | |
61 | } while (0) | |
62 | ||
63 | #define CHECK(ret_code, msg) \ | |
64 | if (ret_code != 0) { \ | |
65 | log_error("Error, %i %s\n", ret_code, msg); \ | |
66 | goto cleanup; \ | |
67 | } | |
68 | ||
69 | static isc_result_t | |
70 | check_credentials(krb5_context context, krb5_ccache ccache, krb5_principal service) | |
71 | { | |
72 | char *realm = NULL; | |
73 | krb5_creds creds; | |
74 | krb5_creds mcreds; | |
75 | krb5_error_code krberr; | |
76 | krb5_timestamp now; | |
77 | isc_result_t result = ISC_R_FAILURE; | |
78 | ||
79 | memset(&mcreds, 0, sizeof(mcreds)); | |
80 | memset(&creds, 0, sizeof(creds)); | |
81 | ||
82 | krberr = krb5_get_default_realm(context, &realm); | |
83 | CHECK_KRB5(context, krberr, "Failed to retrieve default realm"); | |
84 | ||
85 | krberr = krb5_build_principal(context, &mcreds.server, | |
86 | strlen(realm), realm, | |
87 | "krbtgt", realm, NULL); | |
88 | CHECK_KRB5(context, krberr, "Failed to build 'krbtgt/REALM' principal"); | |
89 | ||
90 | mcreds.client = service; | |
91 | ||
92 | krberr = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &creds); | |
93 | ||
94 | if (krberr) { | |
95 | const char * errmsg = krb5_get_error_message(context, krberr); | |
96 | log_error("Credentials are not present in cache (%s)\n", errmsg); | |
97 | krb5_free_error_message(context, errmsg); | |
98 | result = ISC_R_FAILURE; | |
99 | goto cleanup; | |
100 | } | |
101 | CHECK_KRB5(context, krberr, "Credentials are not present in cache "); | |
102 | ||
103 | krberr = krb5_timeofday(context, &now); | |
104 | CHECK_KRB5(context, krberr, "Failed to get time of day"); | |
105 | ||
106 | ||
107 | if (now > (creds.times.endtime + KRB_MIN_TIME)) { | |
108 | log_error("Credentials cache expired"); | |
109 | result = ISC_R_FAILURE; | |
110 | goto cleanup; | |
111 | } else { | |
112 | char buf[255]; | |
113 | char fill = ' '; | |
114 | krb5_timestamp_to_sfstring(creds.times.endtime, buf, 16, &fill); | |
115 | log_info("Credentials valid til %s\n", buf); | |
116 | } | |
117 | ||
118 | result = ISC_R_SUCCESS; | |
119 | ||
120 | cleanup: | |
121 | krb5_free_cred_contents(context, &creds); | |
122 | if (mcreds.server) krb5_free_principal(context, mcreds.server); | |
123 | if (realm) krb5_free_default_realm(context, realm); | |
124 | return result; | |
125 | } | |
126 | ||
127 | isc_result_t | |
128 | krb5_get_tgt(const char *principal, const char *keyfile) | |
129 | { | |
130 | isc_result_t result = ISC_R_FAILURE; | |
131 | char *ccname = NULL; | |
132 | krb5_context context = NULL; | |
133 | krb5_error_code krberr; | |
134 | krb5_ccache ccache = NULL; | |
135 | krb5_principal kprincpw = NULL; | |
136 | krb5_creds my_creds; | |
137 | krb5_creds * my_creds_ptr = NULL; | |
138 | krb5_get_init_creds_opt options; | |
139 | krb5_keytab keytab = NULL; | |
140 | int ret; | |
141 | ||
142 | if (keyfile == NULL || keyfile[0] == '\0') { | |
143 | keyfile = KRB_DEFAULT_KEYTAB; | |
144 | log_info("Using default keytab %s\n", keyfile); | |
145 | } else { | |
146 | if (strncmp(keyfile, "FILE:", 5) != 0) { | |
147 | log_error("Unknown keytab path format: Does it start with FILE:?\n"); | |
148 | return ISC_R_FAILURE; | |
149 | } | |
150 | } | |
151 | ||
152 | krberr = krb5_init_context(&context); | |
153 | CHECK_KRB5(NULL, krberr, "Kerberos context initialization failed"); | |
154 | ||
155 | result = ISC_R_SUCCESS; | |
156 | ||
157 | ccname = "MEMORY:dhcp_ld_krb5_cc"; | |
158 | log_info("Using ccache %s\n" , ccname); | |
159 | ||
160 | ret = setenv("KRB5CCNAME", ccname, 1); | |
161 | if (ret == -1) { | |
162 | log_error("Failed to setup environment\n"); | |
163 | result = ISC_R_FAILURE; | |
164 | goto cleanup; | |
165 | } | |
166 | ||
167 | krberr = krb5_cc_resolve(context, ccname, &ccache); | |
168 | CHECK_KRB5(context, krberr, "Couldnt resolve ccache '%s'", ccname); | |
169 | ||
170 | krberr = krb5_parse_name(context, principal, &kprincpw); | |
171 | CHECK_KRB5(context, krberr, "Failed to parse princ '%s'", princpal); | |
172 | ||
173 | result = check_credentials(context, ccache, kprincpw); | |
174 | if (result == ISC_R_SUCCESS) { | |
175 | log_info("Found valid kerberos credentials\n"); | |
176 | goto cleanup; | |
177 | } else { | |
178 | log_error("No valid krb5 credentials\n"); | |
179 | } | |
180 | ||
181 | krberr = krb5_kt_resolve(context, keyfile, &keytab); | |
182 | CHECK_KRB5(context, krberr, | |
183 | "Failed to resolve kt files '%s'\n", keyfile); | |
184 | ||
185 | memset(&my_creds, 0, sizeof(my_creds)); | |
186 | memset(&options, 0, sizeof(options)); | |
187 | ||
188 | krb5_get_init_creds_opt_set_tkt_life(&options, KRB_MIN_TIME * 2); | |
189 | krb5_get_init_creds_opt_set_address_list(&options, NULL); | |
190 | krb5_get_init_creds_opt_set_forwardable(&options, 0); | |
191 | krb5_get_init_creds_opt_set_proxiable(&options, 0); | |
192 | ||
193 | krberr = krb5_get_init_creds_keytab(context, &my_creds, kprincpw, | |
194 | keytab, 0, NULL, &options); | |
195 | CHECK_KRB5(context, krberr, "Failed to get initial credentials TGT\n"); | |
196 | ||
197 | my_creds_ptr = &my_creds; | |
198 | ||
199 | krberr = krb5_cc_initialize(context, ccache, kprincpw); | |
200 | CHECK_KRB5(context, krberr, "Failed to init ccache\n"); | |
201 | ||
202 | krberr = krb5_cc_store_cred(context, ccache, &my_creds); | |
203 | CHECK_KRB5(context, krberr, "Failed to store credentials\n"); | |
204 | ||
205 | result = ISC_R_SUCCESS; | |
206 | log_info("Successfully init krb tgt %s", principal); | |
207 | ||
208 | cleanup: | |
209 | if (ccache) krb5_cc_close(context, ccache); | |
210 | if (keytab) krb5_kt_close(context, keytab); | |
211 | if (kprincpw) krb5_free_principal(context, kprincpw); | |
212 | if (my_creds_ptr) krb5_free_cred_contents(context, &my_creds); | |
213 | if (context) krb5_free_context(context); | |
214 | return result; | |
215 | } | |
216 | ||
217 | #endif /* defined(LDAP_USE_GSSAPI) */ |