xref: /illumos-gate/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c (revision a194faf8907a6722dcf10ad16c6ca72c9b7bd0ba)
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 2007 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 	return (code);
126 }
127 
128 /*
129  * smb_krb5_open_wrfile
130  *
131  * Open the keytab file for writing.
132  * The keytab should be closed by calling krb5_kt_close().
133  */
134 static int
135 smb_krb5_open_wrfile(krb5_context ctx, char *fname, krb5_keytab *kt)
136 {
137 	char *ktname;
138 	int len;
139 
140 	*kt = NULL;
141 	len = snprintf(NULL, 0, "WRFILE:%s", fname) + 1;
142 	if ((ktname = malloc(len)) == NULL) {
143 		syslog(LOG_ERR, "smb_krb5_write_keytab: resource shortage");
144 		return (-1);
145 	}
146 
147 	(void) snprintf(ktname, len, "WRFILE:%s", fname);
148 
149 	if (krb5_kt_resolve(ctx, ktname, kt) != 0) {
150 		syslog(LOG_ERR, "smb_krb5_write_keytab: failed to open/create "
151 		    "keytab %s\n", fname);
152 		free(ktname);
153 		return (-1);
154 	}
155 
156 	free(ktname);
157 	return (0);
158 }
159 
160 /*
161  * smb_krb5_remove_keytab_entries
162  *
163  * Remove the keys from the keytab for the specified principal.
164  */
165 int
166 smb_krb5_remove_keytab_entries(krb5_context ctx, krb5_principal princ,
167     char *fname)
168 {
169 	krb5_keytab kt = NULL;
170 	int rc = 0;
171 	krb5_error_code code;
172 
173 	if (smb_krb5_open_wrfile(ctx, fname, &kt) != 0)
174 		return (-1);
175 
176 	if ((code = smb_krb5_ktremove(ctx, kt, princ)) != 0) {
177 		syslog(LOG_ERR, "smb_krb5_remove_keytab_entries: %s",
178 		    error_message(code));
179 		rc = -1;
180 	}
181 
182 	krb5_kt_close(ctx, kt);
183 	return (rc);
184 }
185 
186 /*
187  * smb_krb5_update_keytab_entries
188  *
189  * Update the keys for the specified principal in the keytab.
190  * Returns 0 on success.  Otherwise, returns -1.
191  */
192 int
193 smb_krb5_update_keytab_entries(krb5_context ctx, krb5_principal princ,
194     char *fname, krb5_kvno kvno, char *passwd, krb5_enctype *enctypes,
195     int enctype_count)
196 {
197 	krb5_keytab kt = NULL;
198 	int rc = 0, i;
199 	krb5_error_code code;
200 
201 	if (smb_krb5_open_wrfile(ctx, fname, &kt) != 0)
202 		return (-1);
203 
204 	if ((code = smb_krb5_ktremove(ctx, kt, princ)) != 0) {
205 		syslog(LOG_ERR, "smb_krb5_update_keytab_entries: %s",
206 		    error_message(code));
207 		krb5_kt_close(ctx, kt);
208 		return (-1);
209 	}
210 
211 	for (i = 0; i < enctype_count; i++) {
212 		if (smb_krb5_ktadd(ctx, kt, princ, enctypes[i], kvno, passwd)
213 		    != 0) {
214 			rc = -1;
215 			break;
216 		}
217 
218 	}
219 
220 	krb5_kt_close(ctx, kt);
221 	return (rc);
222 }
223 
224 /*
225  * smb_krb5_ktremove
226  *
227  * Removes the old entries for the specified principal from the keytab.
228  *
229  * Returns 0 upon success. Otherwise, returns KRB5 error code.
230  */
231 static krb5_error_code
232 smb_krb5_ktremove(krb5_context ctx, krb5_keytab kt, const krb5_principal princ)
233 {
234 	krb5_keytab_entry entry;
235 	krb5_kt_cursor cursor;
236 	int code;
237 
238 	code = krb5_kt_get_entry(ctx, kt, princ, 0, 0, &entry);
239 	if (code != 0) {
240 		if (code == ENOENT || code == KRB5_KT_NOTFOUND)
241 			return (0);
242 
243 		return (code);
244 	}
245 
246 	krb5_kt_free_entry(ctx, &entry);
247 
248 	if ((code = krb5_kt_start_seq_get(ctx, kt, &cursor)) != 0)
249 		return (code);
250 
251 	while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) {
252 		if (krb5_principal_compare(ctx, princ, entry.principal)) {
253 
254 			code = krb5_kt_end_seq_get(ctx, kt, &cursor);
255 			if (code != 0) {
256 				krb5_kt_free_entry(ctx, &entry);
257 				return (code);
258 			}
259 
260 			code = krb5_kt_remove_entry(ctx, kt, &entry);
261 			if (code != 0) {
262 				krb5_kt_free_entry(ctx, &entry);
263 				return (code);
264 			}
265 
266 			code = krb5_kt_start_seq_get(ctx, kt, &cursor);
267 			if (code != 0) {
268 				krb5_kt_free_entry(ctx, &entry);
269 				return (code);
270 			}
271 
272 		}
273 		krb5_kt_free_entry(ctx, &entry);
274 	}
275 
276 	if (code && code != KRB5_KT_END) {
277 		(void) krb5_kt_end_seq_get(ctx, kt, &cursor);
278 		return (code);
279 	}
280 
281 	if ((code = krb5_kt_end_seq_get(ctx, kt, &cursor)))
282 		return (code);
283 
284 	return (0);
285 }
286 
287 /*
288  * smb_krb5_ktadd
289  *
290  * Add a Keberos key to the keytab file.
291  * Returns 0 on success. Otherwise, returns -1.
292  */
293 static int
294 smb_krb5_ktadd(krb5_context ctx, krb5_keytab kt, const krb5_principal princ,
295 	krb5_enctype enctype, krb5_kvno kvno, const char *pw)
296 {
297 	krb5_keytab_entry *entry;
298 	krb5_data password, salt;
299 	krb5_keyblock key;
300 	krb5_error_code code;
301 	char buf[100];
302 	int rc = 0;
303 
304 	if ((code = krb5_enctype_to_string(enctype, buf, sizeof (buf)))) {
305 		syslog(LOG_ERR, "smb_krb5_ktadd[%d]: unknown enctype",
306 		    enctype);
307 		return (-1);
308 	}
309 
310 	if ((entry = (krb5_keytab_entry *) malloc(sizeof (*entry))) == NULL) {
311 		syslog(LOG_ERR, "smb_krb5_ktadd[%d]: resource shortage",
312 		    enctype);
313 		return (-1);
314 	}
315 
316 	(void) memset((char *)entry, 0, sizeof (*entry));
317 
318 	password.length = strlen(pw);
319 	password.data = (char *)pw;
320 
321 	if ((code = krb5_principal2salt(ctx, princ, &salt)) != 0) {
322 		syslog(LOG_ERR, "smb_krb5_ktadd[%d]: failed to compute salt",
323 		    enctype);
324 		free(entry);
325 		return (-1);
326 	}
327 
328 	code = krb5_c_string_to_key(ctx, enctype, &password, &salt, &key);
329 	krb5_xfree(salt.data);
330 	if (code != 0) {
331 		syslog(LOG_ERR, "smb_krb5_ktadd[%d]: failed to generate key",
332 		    enctype);
333 		free(entry);
334 		return (-1);
335 	}
336 
337 	(void) memcpy(&entry->key, &key, sizeof (krb5_keyblock));
338 	entry->vno = kvno;
339 	entry->principal = princ;
340 
341 	if ((code = krb5_kt_add_entry(ctx, kt, entry)) != 0) {
342 		syslog(LOG_ERR, "smb_krb5_ktadd[%d] failed to add entry to "
343 		    "keytab (%d)", enctype, code);
344 		rc = -1;
345 	}
346 
347 	free(entry);
348 	return (rc);
349 }
350