xref: /illumos-gate/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c (revision 5bb86dd8f405a48942aaaab3ca1f410ed7e6db4d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <unistd.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <syslog.h>
37 #include <kerberosv5/krb5.h>
38 #include <kerberosv5/com_err.h>
39 #include <smbns_krb.h>
40 
41 static int smb_krb5_open_wrfile(krb5_context ctx, char *fname,
42     krb5_keytab *kt);
43 static int smb_krb5_ktadd(krb5_context ctx, krb5_keytab kt,
44     const krb5_principal princ, krb5_enctype enctype, krb5_kvno kvno,
45     const char *pw);
46 static krb5_error_code smb_krb5_ktremove(krb5_context ctx, krb5_keytab kt,
47     const krb5_principal princ);
48 
49 
50 /*
51  * smb_krb5_ctx_init
52  *
53  * Initialize the kerberos context.
54  * Return 0 on success. Otherwise, return -1.
55  */
56 int
57 smb_krb5_ctx_init(krb5_context *ctx)
58 {
59 	if (krb5_init_context(ctx) != 0)
60 		return (-1);
61 
62 	return (0);
63 }
64 
65 /*
66  * smb_krb5_get_principal
67  *
68  * Setup the krb5_principal given the host principal in string format.
69  * Return 0 on success. Otherwise, return -1.
70  */
71 int
72 smb_krb5_get_principal(krb5_context ctx, char *princ_str, krb5_principal *princ)
73 {
74 	if (krb5_parse_name(ctx, princ_str, princ) != 0)
75 		return (-1);
76 
77 	return (0);
78 }
79 
80 /*
81  * smb_krb5_ctx_fini
82  *
83  * Free the kerberos context.
84  */
85 void
86 smb_krb5_ctx_fini(krb5_context ctx)
87 {
88 	krb5_free_context(ctx);
89 }
90 
91 /*
92  * smb_ksetpw
93  *
94  * Set the workstation trust account password.
95  * Returns 0 on success.  Otherwise, returns non-zero value.
96  */
97 int
98 smb_krb5_setpwd(krb5_context ctx, krb5_principal princ, char *passwd)
99 {
100 	krb5_error_code code;
101 	krb5_ccache cc = NULL;
102 	int result_code;
103 	krb5_data result_code_string, result_string;
104 
105 	(void) memset(&result_code_string, 0, sizeof (result_code_string));
106 	(void) memset(&result_string, 0, sizeof (result_string));
107 
108 	if ((code = krb5_cc_default(ctx, &cc)) != 0) {
109 		syslog(LOG_ERR, "smb_krb5_setpwd: failed to find a ccache\n");
110 		return (-1);
111 	}
112 
113 	code = krb5_set_password_using_ccache(ctx, cc, passwd, princ,
114 	    &result_code, &result_code_string, &result_string);
115 
116 	krb5_cc_close(ctx, cc);
117 
118 	if (code != 0)
119 		(void) syslog(LOG_ERR,
120 		    "smb_krb5_setpwd: Result: %.*s (%d) %.*s\n",
121 		    result_code == 0 ?
122 		    strlen("success") : result_code_string.length,
123 		    result_code == 0 ? "success" : result_code_string.data,
124 		    result_code, result_string.length, result_string.data);
125 
126 	free(result_code_string.data);
127 	free(result_string.data);
128 	return (code);
129 }
130 
131 /*
132  * smb_krb5_open_wrfile
133  *
134  * Open the keytab file for writing.
135  * The keytab should be closed by calling krb5_kt_close().
136  */
137 static int
138 smb_krb5_open_wrfile(krb5_context ctx, char *fname, krb5_keytab *kt)
139 {
140 	char *ktname;
141 	int len;
142 
143 	*kt = NULL;
144 	len = snprintf(NULL, 0, "WRFILE:%s", fname) + 1;
145 	if ((ktname = malloc(len)) == NULL) {
146 		syslog(LOG_ERR, "smb_krb5_write_keytab: resource shortage");
147 		return (-1);
148 	}
149 
150 	(void) snprintf(ktname, len, "WRFILE:%s", fname);
151 
152 	if (krb5_kt_resolve(ctx, ktname, kt) != 0) {
153 		syslog(LOG_ERR, "smb_krb5_write_keytab: failed to open/create "
154 		    "keytab %s\n", fname);
155 		free(ktname);
156 		return (-1);
157 	}
158 
159 	free(ktname);
160 	return (0);
161 }
162 
163 /*
164  * smb_krb5_remove_keytab_entries
165  *
166  * Remove the keys from the keytab for the specified principal.
167  */
168 int
169 smb_krb5_remove_keytab_entries(krb5_context ctx, krb5_principal princ,
170     char *fname)
171 {
172 	krb5_keytab kt = NULL;
173 	int rc = 0;
174 	krb5_error_code code;
175 
176 	if (smb_krb5_open_wrfile(ctx, fname, &kt) != 0)
177 		return (-1);
178 
179 	if ((code = smb_krb5_ktremove(ctx, kt, princ)) != 0) {
180 		syslog(LOG_ERR, "smb_krb5_remove_keytab_entries: %s",
181 		    error_message(code));
182 		rc = -1;
183 	}
184 
185 	krb5_kt_close(ctx, kt);
186 	return (rc);
187 }
188 
189 /*
190  * smb_krb5_update_keytab_entries
191  *
192  * Update the keys for the specified principal in the keytab.
193  * Returns 0 on success.  Otherwise, returns -1.
194  */
195 int
196 smb_krb5_update_keytab_entries(krb5_context ctx, krb5_principal princ,
197     char *fname, krb5_kvno kvno, char *passwd, krb5_enctype *enctypes,
198     int enctype_count)
199 {
200 	krb5_keytab kt = NULL;
201 	int rc = 0, i;
202 	krb5_error_code code;
203 
204 	if (smb_krb5_open_wrfile(ctx, fname, &kt) != 0)
205 		return (-1);
206 
207 	if ((code = smb_krb5_ktremove(ctx, kt, princ)) != 0) {
208 		syslog(LOG_ERR, "smb_krb5_update_keytab_entries: %s",
209 		    error_message(code));
210 		krb5_kt_close(ctx, kt);
211 		return (-1);
212 	}
213 
214 	for (i = 0; i < enctype_count; i++) {
215 		if (smb_krb5_ktadd(ctx, kt, princ, enctypes[i], kvno, passwd)
216 		    != 0) {
217 			rc = -1;
218 			break;
219 		}
220 
221 	}
222 
223 	krb5_kt_close(ctx, kt);
224 	return (rc);
225 }
226 
227 /*
228  * smb_krb5_ktremove
229  *
230  * Removes the old entries for the specified principal from the keytab.
231  *
232  * Returns 0 upon success. Otherwise, returns KRB5 error code.
233  */
234 static krb5_error_code
235 smb_krb5_ktremove(krb5_context ctx, krb5_keytab kt, const krb5_principal princ)
236 {
237 	krb5_keytab_entry entry;
238 	krb5_kt_cursor cursor;
239 	int code;
240 
241 	code = krb5_kt_get_entry(ctx, kt, princ, 0, 0, &entry);
242 	if (code != 0) {
243 		if (code == ENOENT || code == KRB5_KT_NOTFOUND)
244 			return (0);
245 
246 		return (code);
247 	}
248 
249 	krb5_kt_free_entry(ctx, &entry);
250 
251 	if ((code = krb5_kt_start_seq_get(ctx, kt, &cursor)) != 0)
252 		return (code);
253 
254 	while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) {
255 		if (krb5_principal_compare(ctx, princ, entry.principal)) {
256 
257 			code = krb5_kt_end_seq_get(ctx, kt, &cursor);
258 			if (code != 0) {
259 				krb5_kt_free_entry(ctx, &entry);
260 				return (code);
261 			}
262 
263 			code = krb5_kt_remove_entry(ctx, kt, &entry);
264 			if (code != 0) {
265 				krb5_kt_free_entry(ctx, &entry);
266 				return (code);
267 			}
268 
269 			code = krb5_kt_start_seq_get(ctx, kt, &cursor);
270 			if (code != 0) {
271 				krb5_kt_free_entry(ctx, &entry);
272 				return (code);
273 			}
274 
275 		}
276 		krb5_kt_free_entry(ctx, &entry);
277 	}
278 
279 	if (code && code != KRB5_KT_END) {
280 		(void) krb5_kt_end_seq_get(ctx, kt, &cursor);
281 		return (code);
282 	}
283 
284 	if ((code = krb5_kt_end_seq_get(ctx, kt, &cursor)))
285 		return (code);
286 
287 	return (0);
288 }
289 
290 /*
291  * smb_krb5_ktadd
292  *
293  * Add a Keberos key to the keytab file.
294  * Returns 0 on success. Otherwise, returns -1.
295  */
296 static int
297 smb_krb5_ktadd(krb5_context ctx, krb5_keytab kt, const krb5_principal princ,
298 	krb5_enctype enctype, krb5_kvno kvno, const char *pw)
299 {
300 	krb5_keytab_entry *entry;
301 	krb5_data password, salt;
302 	krb5_keyblock key;
303 	krb5_error_code code;
304 	char buf[100];
305 	int rc = 0;
306 
307 	if ((code = krb5_enctype_to_string(enctype, buf, sizeof (buf)))) {
308 		syslog(LOG_ERR, "smb_krb5_ktadd[%d]: unknown enctype",
309 		    enctype);
310 		return (-1);
311 	}
312 
313 	if ((entry = (krb5_keytab_entry *) malloc(sizeof (*entry))) == NULL) {
314 		syslog(LOG_ERR, "smb_krb5_ktadd[%d]: resource shortage",
315 		    enctype);
316 		return (-1);
317 	}
318 
319 	(void) memset((char *)entry, 0, sizeof (*entry));
320 
321 	password.length = strlen(pw);
322 	password.data = (char *)pw;
323 
324 	if ((code = krb5_principal2salt(ctx, princ, &salt)) != 0) {
325 		syslog(LOG_ERR, "smb_krb5_ktadd[%d]: failed to compute salt",
326 		    enctype);
327 		free(entry);
328 		return (-1);
329 	}
330 
331 	code = krb5_c_string_to_key(ctx, enctype, &password, &salt, &key);
332 	krb5_xfree(salt.data);
333 	if (code != 0) {
334 		syslog(LOG_ERR, "smb_krb5_ktadd[%d]: failed to generate key",
335 		    enctype);
336 		free(entry);
337 		return (-1);
338 	}
339 
340 	(void) memcpy(&entry->key, &key, sizeof (krb5_keyblock));
341 	entry->vno = kvno;
342 	entry->principal = princ;
343 
344 	if ((code = krb5_kt_add_entry(ctx, kt, entry)) != 0) {
345 		syslog(LOG_ERR, "smb_krb5_ktadd[%d] failed to add entry to "
346 		    "keytab (%d)", enctype, code);
347 		rc = -1;
348 	}
349 
350 	free(entry);
351 	if (key.length)
352 		krb5_free_keyblock_contents(ctx, &key);
353 	return (rc);
354 }
355