2b009baa58da74f85268d26f6f13405e67da5103
[sfrench/samba-autobuild/.git] / source3 / auth / user_krb5.c
1 /*
2    Unix SMB/CIFS implementation.
3    Authentication utility functions
4    Copyright (C) Simo Sorce 2010
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "auth.h"
22 #include "librpc/gen_ndr/krb5pac.h"
23 #include "nsswitch/libwbclient/wbclient.h"
24 #include "passdb.h"
25 #include "lib/param/loadparm.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_AUTH
29
30 #ifdef HAVE_KRB5
31 NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx,
32                                      const char *cli_name,
33                                      const char *princ_name,
34                                      struct PAC_LOGON_INFO *logon_info,
35                                      bool *is_mapped,
36                                      bool *mapped_to_guest,
37                                      char **ntuser,
38                                      char **ntdomain,
39                                      char **username,
40                                      struct passwd **_pw)
41 {
42         NTSTATUS status;
43         char *domain = NULL;
44         char *realm = NULL;
45         char *user = NULL;
46         char *p;
47         char *fuser = NULL;
48         char *unixuser = NULL;
49         struct passwd *pw = NULL;
50
51         DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name));
52
53         p = strchr_m(princ_name, '@');
54         if (!p) {
55                 DEBUG(3, ("[%s] Doesn't look like a valid principal\n",
56                           princ_name));
57                 return NT_STATUS_LOGON_FAILURE;
58         }
59
60         user = talloc_strndup(mem_ctx, princ_name, p - princ_name);
61         if (!user) {
62                 return NT_STATUS_NO_MEMORY;
63         }
64
65         realm = talloc_strdup(talloc_tos(), p + 1);
66         if (!realm) {
67                 return NT_STATUS_NO_MEMORY;
68         }
69
70         if (!strequal(realm, lp_realm())) {
71                 DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm));
72                 if (!lp_allow_trusted_domains()) {
73                         return NT_STATUS_LOGON_FAILURE;
74                 }
75         }
76
77         if (logon_info && logon_info->info3.base.logon_domain.string) {
78                 domain = talloc_strdup(mem_ctx,
79                                         logon_info->info3.base.logon_domain.string);
80                 if (!domain) {
81                         return NT_STATUS_NO_MEMORY;
82                 }
83                 DEBUG(10, ("Domain is [%s] (using PAC)\n", domain));
84         } else {
85
86                 /* If we have winbind running, we can (and must) shorten the
87                    username by using the short netbios name. Otherwise we will
88                    have inconsistent user names. With Kerberos, we get the
89                    fully qualified realm, with ntlmssp we get the short
90                    name. And even w2k3 does use ntlmssp if you for example
91                    connect to an ip address. */
92
93                 wbcErr wbc_status;
94                 struct wbcDomainInfo *info = NULL;
95
96                 DEBUG(10, ("Mapping [%s] to short name using winbindd\n",
97                            realm));
98
99                 wbc_status = wbcDomainInfo(realm, &info);
100
101                 if (WBC_ERROR_IS_OK(wbc_status)) {
102                         domain = talloc_strdup(mem_ctx,
103                                                 info->short_name);
104                         wbcFreeMemory(info);
105                 } else {
106                         DEBUG(3, ("Could not find short name: %s\n",
107                                   wbcErrorString(wbc_status)));
108                         domain = talloc_strdup(mem_ctx, realm);
109                 }
110                 if (!domain) {
111                         return NT_STATUS_NO_MEMORY;
112                 }
113                 DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain));
114         }
115
116         fuser = talloc_asprintf(mem_ctx,
117                                 "%s%c%s",
118                                 domain,
119                                 *lp_winbind_separator(),
120                                 user);
121         if (!fuser) {
122                 return NT_STATUS_NO_MEMORY;
123         }
124
125         *is_mapped = map_username(mem_ctx, fuser, &fuser);
126         if (!fuser) {
127                 return NT_STATUS_NO_MEMORY;
128         }
129         *mapped_to_guest = false;
130
131         pw = smb_getpwnam(mem_ctx, fuser, &unixuser, true);
132         if (pw) {
133                 if (!unixuser) {
134                         return NT_STATUS_NO_MEMORY;
135                 }
136                 /* if a real user check pam account restrictions */
137                 /* only really perfomed if "obey pam restriction" is true */
138                 /* do this before an eventual mapping to guest occurs */
139                 status = smb_pam_accountcheck(pw->pw_name, cli_name);
140                 if (!NT_STATUS_IS_OK(status)) {
141                         DEBUG(1, ("PAM account restrictions prevent user "
142                                   "[%s] login\n", unixuser));
143                         return status;
144                 }
145         }
146         if (!pw) {
147
148                 /* this was originally the behavior of Samba 2.2, if a user
149                    did not have a local uid but has been authenticated, then
150                    map them to a guest account */
151
152                 if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_UID) {
153                         *mapped_to_guest = true;
154                         fuser = talloc_strdup(mem_ctx, lp_guest_account());
155                         if (!fuser) {
156                                 return NT_STATUS_NO_MEMORY;
157                         }
158                         pw = smb_getpwnam(mem_ctx, fuser, &unixuser, true);
159                 }
160
161                 /* extra sanity check that the guest account is valid */
162                 if (!pw) {
163                         DBG_NOTICE("Username %s is invalid on this system\n",
164                                   fuser);
165                         return NT_STATUS_LOGON_FAILURE;
166                 }
167         }
168
169         if (!unixuser) {
170                 return NT_STATUS_NO_MEMORY;
171         }
172
173         *username = talloc_strdup(mem_ctx, unixuser);
174         if (!*username) {
175                 return NT_STATUS_NO_MEMORY;
176         }
177         *ntuser = user;
178         *ntdomain = domain;
179         *_pw = pw;
180
181         return NT_STATUS_OK;
182 }
183
184 NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx,
185                                 char *ntuser,
186                                 char *ntdomain,
187                                 char *username,
188                                 struct passwd *pw,
189                                 const struct netr_SamInfo3 *info3,
190                                 bool mapped_to_guest, bool username_was_mapped,
191                                 DATA_BLOB *session_key,
192                                 struct auth_session_info **session_info)
193 {
194         NTSTATUS status;
195         struct auth_serversupplied_info *server_info;
196
197         if (mapped_to_guest) {
198                 status = make_server_info_guest(mem_ctx, &server_info);
199                 if (!NT_STATUS_IS_OK(status)) {
200                         DEBUG(1, ("make_server_info_guest failed: %s!\n",
201                                   nt_errstr(status)));
202                         return status;
203                 }
204
205         } else if (info3) {
206                 /* pass the unmapped username here since map_username()
207                    will be called again in make_server_info_info3() */
208
209                 status = make_server_info_info3(mem_ctx,
210                                                 ntuser, ntdomain,
211                                                 &server_info,
212                                                 info3);
213                 if (!NT_STATUS_IS_OK(status)) {
214                         DEBUG(1, ("make_server_info_info3 failed: %s!\n",
215                                   nt_errstr(status)));
216                         return status;
217                 }
218
219         } else {
220                 /*
221                  * We didn't get a PAC, we have to make up the user
222                  * ourselves. Try to ask the pdb backend to provide
223                  * SID consistency with ntlmssp session setup
224                  */
225                 struct samu *sampass;
226
227                 sampass = samu_new(talloc_tos());
228                 if (sampass == NULL) {
229                         return NT_STATUS_NO_MEMORY;
230                 }
231
232                 if (pdb_getsampwnam(sampass, username)) {
233                         DEBUG(10, ("found user %s in passdb, calling "
234                                    "make_server_info_sam\n", username));
235                         status = make_server_info_sam(mem_ctx,
236                                                       sampass,
237                                                       &server_info);
238                 } else {
239                         /*
240                          * User not in passdb, make it up artificially
241                          */
242                         DEBUG(10, ("didn't find user %s in passdb, calling "
243                                    "make_server_info_pw\n", username));
244                         status = make_server_info_pw(mem_ctx,
245                                                      username,
246                                                      pw,
247                                                      &server_info);
248                 }
249
250                 TALLOC_FREE(sampass);
251
252                 if (!NT_STATUS_IS_OK(status)) {
253                         DEBUG(1, ("make_server_info_[sam|pw] failed: %s!\n",
254                                   nt_errstr(status)));
255                         return status;
256                 }
257
258                 /* make_server_info_pw does not set the domain. Without this
259                  * we end up with the local netbios name in substitutions for
260                  * %D. */
261
262                 if (server_info->info3 != NULL) {
263                         server_info->info3->base.logon_domain.string =
264                                 talloc_strdup(server_info->info3, ntdomain);
265                 }
266         }
267
268         server_info->nss_token |= username_was_mapped;
269
270         status = create_local_token(mem_ctx, server_info, session_key, ntuser, session_info);
271         talloc_free(server_info);
272         if (!NT_STATUS_IS_OK(status)) {
273                 DEBUG(10,("failed to create local token: %s\n",
274                           nt_errstr(status)));
275                 return status;
276         }
277
278         return NT_STATUS_OK;
279 }
280
281 #else /* HAVE_KRB5 */
282 NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx,
283                                      const char *cli_name,
284                                      const char *princ_name,
285                                      struct PAC_LOGON_INFO *logon_info,
286                                      bool *is_mapped,
287                                      bool *mapped_to_guest,
288                                      char **ntuser,
289                                      char **ntdomain,
290                                      char **username,
291                                      struct passwd **_pw)
292 {
293         return NT_STATUS_NOT_IMPLEMENTED;
294 }
295
296 NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx,
297                                 char *ntuser,
298                                 char *ntdomain,
299                                 char *username,
300                                 struct passwd *pw,
301                                 const struct netr_SamInfo3 *info3,
302                                 bool mapped_to_guest, bool username_was_mapped,
303                                 DATA_BLOB *session_key,
304                                 struct auth_session_info **session_info)
305 {
306         return NT_STATUS_NOT_IMPLEMENTED;
307 }
308
309 #endif /* HAVE_KRB5 */