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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
25 */
26/*
27 * Copyright 1990 by the Massachusetts Institute of Technology.
28 * All Rights Reserved.
29 *
30 * Export of this software from the United States of America may
31 *   require a specific license from the United States Government.
32 *   It is the responsibility of any person or organization contemplating
33 *   export to obtain such a license before exporting.
34 *
35 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
36 * distribute this software and its documentation for any purpose and
37 * without fee is hereby granted, provided that the above copyright
38 * notice appear in all copies and that both that copyright notice and
39 * this permission notice appear in supporting documentation, and that
40 * the name of M.I.T. not be used in advertising or publicity pertaining
41 * to distribution of the software without specific, written prior
42 * permission.  Furthermore if you modify this software you must label
43 * your software as modified software and not distribute it in such a
44 * fashion that it might be confused with the original M.I.T. software.
45 * M.I.T. makes no representations about the suitability of
46 * this software for any purpose.  It is provided "as is" without express
47 * or implied warranty.
48 *
49 *
50 * Initialize a credentials cache.
51 */
52#include <kerberosv5/krb5.h>
53#include <kerberosv5/com_err.h>
54#include <assert.h>
55#include <stdio.h>
56#include <syslog.h>
57#include <errno.h>
58
59#include <smbsrv/libsmbns.h>
60#include <smbns_krb.h>
61
62int
63smb_kinit(char *domain_name, char *principal_name, char *principal_passwd)
64{
65	char default_realm[MAXHOSTNAMELEN];
66	krb5_context ctx = NULL;
67	krb5_ccache cc = NULL;
68	krb5_principal me = NULL;
69	krb5_creds my_creds;
70	krb5_error_code code;
71	const char *errmsg = NULL;
72	const char *doing = NULL;
73	smb_ads_status_t err;
74
75	assert(principal_name != NULL);
76	assert(principal_passwd != NULL);
77
78	(void) memset(&my_creds, 0, sizeof (my_creds));
79
80	/*
81	 * From this point on, we can goto cleanup because the key variables
82	 * are initialized.
83	 */
84
85	code = krb5_init_context(&ctx);
86	if (code) {
87		err = SMB_ADS_KRB5_INIT_CTX;
88		doing = "smbns_krb: initializing context";
89		goto cleanup;
90	}
91
92	/*
93	 * In case krb5.conf is not configured, set the default realm.
94	 */
95	(void) strlcpy(default_realm, domain_name, sizeof (default_realm));
96	(void) smb_strupr(default_realm);
97	(void) krb5_set_default_realm(ctx, default_realm);
98
99	code = krb5_cc_default(ctx, &cc);
100	if (code != 0) {
101		err = SMB_ADS_KRB5_CC_DEFAULT;
102		doing = "smbns_krb: resolve default credentials cache";
103		goto cleanup;
104	}
105
106	/* Use specified name */
107	code = krb5_parse_name(ctx, principal_name, &me);
108	if (code != 0) {
109		err = SMB_ADS_KRB5_PARSE_PRINCIPAL;
110		doing = "smbns_krb: parsing principal name";
111		goto cleanup;
112	}
113
114	code = krb5_get_init_creds_password(ctx, &my_creds, me,
115	    principal_passwd, NULL, 0, (krb5_deltat)0,
116	    NULL, NULL);
117	if (code != 0) {
118		err = SMB_ADS_KRB5_GET_INIT_CREDS_PW;
119		doing = "smbns_krb: getting initial credentials";
120
121		if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
122			errmsg = "smbns_krb: Password incorrect";
123		}
124
125		goto cleanup;
126	}
127
128	code = krb5_cc_initialize(ctx, cc, me);
129	if (code != 0) {
130		err = SMB_ADS_KRB5_CC_INITIALIZE;
131		doing = "smbns_krb: initializing cache";
132		goto cleanup;
133	}
134
135	code = krb5_cc_store_cred(ctx, cc, &my_creds);
136	if (code != 0) {
137		err = SMB_ADS_KRB5_CC_STORE_CRED;
138		doing = "smbns_krb: storing credentials";
139		goto cleanup;
140	}
141
142	/* SUCCESS! */
143	err = SMB_ADS_SUCCESS;
144
145cleanup:
146	if (code != 0) {
147		if (errmsg == NULL)
148			smb_krb5_log_errmsg(ctx, doing, code);
149		else
150			syslog(LOG_ERR, "%s (%s)", doing, errmsg);
151	}
152
153	if (my_creds.client == me) {
154		my_creds.client = NULL;
155	}
156	krb5_free_cred_contents(ctx, &my_creds);
157
158	if (me)
159		krb5_free_principal(ctx, me);
160	if (cc)
161		(void) krb5_cc_close(ctx, cc);
162	if (ctx)
163		krb5_free_context(ctx);
164
165	return (err);
166}
167
168/*
169 * Invoke krb5_get_error_message() to generate a richer error message.
170 */
171void
172smb_krb5_log_errmsg(krb5_context ctx, const char *prefix, krb5_error_code code)
173{
174	const char *msg;
175
176	msg = krb5_get_error_message(ctx, code);
177	syslog(LOG_ERR, "%s (%s)", prefix, msg);
178	krb5_free_error_message(ctx, msg);
179}
180
181/*
182 * smb_ccache_init
183 *
184 * Creates the directory where the Kerberos ccache file is located
185 * and set KRB5CCNAME in the environment.
186 *
187 * Returns 0 upon succcess.  Otherwise, returns
188 * -1 if it fails to create the specified directory fails.
189 * -2 if it fails to set the KRB5CCNAME environment variable.
190 */
191int
192smb_ccache_init(char *dir, char *filename)
193{
194	static char buf[MAXPATHLEN];
195
196	if ((mkdir(dir, 0700) < 0) && (errno != EEXIST))
197		return (-1);
198
199	(void) snprintf(buf, MAXPATHLEN, "KRB5CCNAME=%s/%s", dir, filename);
200	if (putenv(buf) != 0)
201		return (-2);
202	return (0);
203}
204
205void
206smb_ccache_remove(char *path)
207{
208	if ((remove(path) < 0) && (errno != ENOENT))
209		syslog(LOG_ERR, "smbns_krb: failed to remove ccache (%s)",
210		    path);
211}
212