~vcs-imports/samba/main

« back to all changes in this revision

Viewing changes to source/auth/auth_domain.c

  • Committer: jerry
  • Date: 2006-07-14 21:48:39 UTC
  • Revision ID: vcs-imports@canonical.com-20060714214839-586d8c489a8fcead
gutting trunk to move to svn:externals

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* 
2
 
   Unix SMB/CIFS implementation.
3
 
   Authenticate against a remote domain
4
 
   Copyright (C) Andrew Tridgell 1992-1998
5
 
   Copyright (C) Andrew Bartlett 2001
6
 
   
7
 
   This program is free software; you can redistribute it and/or modify
8
 
   it under the terms of the GNU General Public License as published by
9
 
   the Free Software Foundation; either version 2 of the License, or
10
 
   (at your option) any later version.
11
 
   
12
 
   This program is distributed in the hope that it will be useful,
13
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
   GNU General Public License for more details.
16
 
   
17
 
   You should have received a copy of the GNU General Public License
18
 
   along with this program; if not, write to the Free Software
19
 
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
 
*/
21
 
 
22
 
#include "includes.h"
23
 
 
24
 
#undef DBGC_CLASS
25
 
#define DBGC_CLASS DBGC_AUTH
26
 
 
27
 
extern BOOL global_machine_password_needs_changing;
28
 
 
29
 
/**
30
 
 * Connect to a remote server for (inter)domain security authenticaion.
31
 
 *
32
 
 * @param cli the cli to return containing the active connection
33
 
 * @param server either a machine name or text IP address to
34
 
 *               connect to.
35
 
 * @param setup_creds_as domain account to setup credentials as
36
 
 * @param sec_chan a switch value to distinguish between domain
37
 
 *                 member and interdomain authentication
38
 
 * @param trust_passwd the trust password to establish the
39
 
 *                     credentials with.
40
 
 *
41
 
 **/
42
 
 
43
 
static NTSTATUS connect_to_domain_password_server(struct cli_state **cli,
44
 
                                                const char *domain,
45
 
                                                const char *dc_name,
46
 
                                                struct in_addr dc_ip, 
47
 
                                                struct rpc_pipe_client **pipe_ret,
48
 
                                                BOOL *retry)
49
 
{
50
 
        NTSTATUS result;
51
 
        struct rpc_pipe_client *netlogon_pipe = NULL;
52
 
 
53
 
        *cli = NULL;
54
 
 
55
 
        *pipe_ret = NULL;
56
 
 
57
 
        /* TODO: Send a SAMLOGON request to determine whether this is a valid
58
 
           logonserver.  We can avoid a 30-second timeout if the DC is down
59
 
           if the SAMLOGON request fails as it is only over UDP. */
60
 
 
61
 
        /* we use a mutex to prevent two connections at once - when a 
62
 
           Win2k PDC get two connections where one hasn't completed a 
63
 
           session setup yet it will send a TCP reset to the first 
64
 
           connection (tridge) */
65
 
 
66
 
        /*
67
 
         * With NT4.x DC's *all* authentication must be serialized to avoid
68
 
         * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
69
 
         */
70
 
 
71
 
        if (!grab_server_mutex(dc_name)) {
72
 
                return NT_STATUS_NO_LOGON_SERVERS;
73
 
        }
74
 
        
75
 
        /* Attempt connection */
76
 
        *retry = True;
77
 
        result = cli_full_connection(cli, global_myname(), dc_name, &dc_ip, 0, 
78
 
                "IPC$", "IPC", "", "", "", 0, Undefined, retry);
79
 
 
80
 
        if (!NT_STATUS_IS_OK(result)) {
81
 
                /* map to something more useful */
82
 
                if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
83
 
                        result = NT_STATUS_NO_LOGON_SERVERS;
84
 
                }
85
 
 
86
 
                if (*cli) {
87
 
                        cli_shutdown(*cli);
88
 
                        *cli = NULL;
89
 
                }
90
 
 
91
 
                release_server_mutex();
92
 
                return result;
93
 
        }
94
 
 
95
 
        /*
96
 
         * We now have an anonymous connection to IPC$ on the domain password server.
97
 
         */
98
 
 
99
 
        /*
100
 
         * Even if the connect succeeds we need to setup the netlogon
101
 
         * pipe here. We do this as we may just have changed the domain
102
 
         * account password on the PDC and yet we may be talking to
103
 
         * a BDC that doesn't have this replicated yet. In this case
104
 
         * a successful connect to a DC needs to take the netlogon connect
105
 
         * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>.
106
 
         */
107
 
 
108
 
        /* open the netlogon pipe. */
109
 
        if (lp_client_schannel()) {
110
 
                /* We also setup the creds chain in the open_schannel call. */
111
 
                netlogon_pipe = cli_rpc_pipe_open_schannel(*cli, PI_NETLOGON,
112
 
                                        PIPE_AUTH_LEVEL_PRIVACY, domain, &result);
113
 
        } else {
114
 
                netlogon_pipe = cli_rpc_pipe_open_noauth(*cli, PI_NETLOGON, &result);
115
 
        }
116
 
 
117
 
        if(!netlogon_pipe) {
118
 
                DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
119
 
machine %s. Error was : %s.\n", dc_name, nt_errstr(result)));
120
 
                cli_shutdown(*cli);
121
 
                *cli = NULL;
122
 
                release_server_mutex();
123
 
                return result;
124
 
        }
125
 
 
126
 
        if (!lp_client_schannel()) {
127
 
                /* We need to set up a creds chain on an unauthenticated netlogon pipe. */
128
 
                uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS;
129
 
                uint32 sec_chan_type = 0;
130
 
                unsigned char machine_pwd[16];
131
 
 
132
 
                if (!get_trust_pw(domain, machine_pwd, &sec_chan_type)) {
133
 
                        DEBUG(0, ("connect_to_domain_password_server: could not fetch "
134
 
                        "trust account password for domain '%s'\n",
135
 
                                domain));
136
 
                        cli_shutdown(*cli);
137
 
                        *cli = NULL;
138
 
                        release_server_mutex();
139
 
                        return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
140
 
                }
141
 
 
142
 
                result = rpccli_netlogon_setup_creds(netlogon_pipe,
143
 
                                        dc_name, /* server name */
144
 
                                        domain, /* domain */
145
 
                                        global_myname(), /* client name */
146
 
                                        global_myname(), /* machine account name */
147
 
                                        machine_pwd,
148
 
                                        sec_chan_type,
149
 
                                        &neg_flags);
150
 
 
151
 
                if (!NT_STATUS_IS_OK(result)) {
152
 
                        cli_shutdown(*cli);
153
 
                        *cli = NULL;
154
 
                        release_server_mutex();
155
 
                        return result;
156
 
                }
157
 
        }
158
 
 
159
 
        if(!netlogon_pipe) {
160
 
                DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
161
 
machine %s. Error was : %s.\n", dc_name, cli_errstr(*cli)));
162
 
                cli_shutdown(*cli);
163
 
                *cli = NULL;
164
 
                release_server_mutex();
165
 
                return NT_STATUS_NO_LOGON_SERVERS;
166
 
        }
167
 
 
168
 
        /* We exit here with the mutex *locked*. JRA */
169
 
 
170
 
        *pipe_ret = netlogon_pipe;
171
 
 
172
 
        return NT_STATUS_OK;
173
 
}
174
 
 
175
 
/***********************************************************************
176
 
 Do the same as security=server, but using NT Domain calls and a session
177
 
 key from the machine password.  If the server parameter is specified
178
 
 use it, otherwise figure out a server from the 'password server' param.
179
 
************************************************************************/
180
 
 
181
 
static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
182
 
                                        const auth_usersupplied_info *user_info, 
183
 
                                        const char *domain,
184
 
                                        uchar chal[8],
185
 
                                        auth_serversupplied_info **server_info, 
186
 
                                        const char *dc_name,
187
 
                                        struct in_addr dc_ip)
188
 
 
189
 
{
190
 
        NET_USER_INFO_3 info3;
191
 
        struct cli_state *cli = NULL;
192
 
        struct rpc_pipe_client *netlogon_pipe = NULL;
193
 
        NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS;
194
 
        int i;
195
 
        BOOL retry = True;
196
 
 
197
 
        /*
198
 
         * At this point, smb_apasswd points to the lanman response to
199
 
         * the challenge in local_challenge, and smb_ntpasswd points to
200
 
         * the NT response to the challenge in local_challenge. Ship
201
 
         * these over the secure channel to a domain controller and
202
 
         * see if they were valid.
203
 
         */
204
 
 
205
 
        /* rety loop for robustness */
206
 
        
207
 
        for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++) {
208
 
                nt_status = connect_to_domain_password_server(&cli,
209
 
                                                        domain,
210
 
                                                        dc_name,
211
 
                                                        dc_ip,
212
 
                                                        &netlogon_pipe,
213
 
                                                        &retry);
214
 
        }
215
 
 
216
 
        if ( !NT_STATUS_IS_OK(nt_status) ) {
217
 
                DEBUG(0,("domain_client_validate: Domain password server not available.\n"));
218
 
                if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
219
 
                        return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
220
 
                }
221
 
                return nt_status;
222
 
        }
223
 
 
224
 
        /* store a successful connection */
225
 
 
226
 
        saf_store( domain, cli->desthost );
227
 
 
228
 
        ZERO_STRUCT(info3);
229
 
 
230
 
        /*
231
 
         * If this call succeeds, we now have lots of info about the user
232
 
         * in the info3 structure.  
233
 
         */
234
 
 
235
 
        nt_status = rpccli_netlogon_sam_network_logon(netlogon_pipe,
236
 
                                                      mem_ctx,
237
 
                                                      user_info->logon_parameters,/* flags such as 'allow workstation logon' */ 
238
 
                                                      dc_name,                    /* server name */
239
 
                                                      user_info->smb_name,        /* user name logging on. */
240
 
                                                      user_info->domain,          /* domain name */
241
 
                                                      user_info->wksta_name,      /* workstation name */
242
 
                                                      chal,                       /* 8 byte challenge. */
243
 
                                                      user_info->lm_resp,         /* lanman 24 byte response */
244
 
                                                      user_info->nt_resp,         /* nt 24 byte response */
245
 
                                                      &info3);                    /* info3 out */
246
 
 
247
 
        /* Let go as soon as possible so we avoid any potential deadlocks
248
 
           with winbind lookup up users or groups. */
249
 
           
250
 
        release_server_mutex();
251
 
 
252
 
        if (!NT_STATUS_IS_OK(nt_status)) {
253
 
                DEBUG(0,("domain_client_validate: unable to validate password "
254
 
                         "for user %s in domain %s to Domain controller %s. "
255
 
                         "Error was %s.\n", user_info->smb_name,
256
 
                         user_info->domain, dc_name, 
257
 
                         nt_errstr(nt_status)));
258
 
 
259
 
                /* map to something more useful */
260
 
                if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
261
 
                        nt_status = NT_STATUS_NO_LOGON_SERVERS;
262
 
                }
263
 
        } else {
264
 
                nt_status = make_server_info_info3(mem_ctx,
265
 
                                                user_info->smb_name,
266
 
                                                domain,
267
 
                                                server_info,
268
 
                                                &info3);
269
 
 
270
 
                if (NT_STATUS_IS_OK(nt_status)) {
271
 
                        (*server_info)->was_mapped |= user_info->was_mapped;
272
 
                }
273
 
 
274
 
                netsamlogon_cache_store( user_info->smb_name, &info3 );
275
 
        }
276
 
 
277
 
        /* Note - once the cli stream is shutdown the mem_ctx used
278
 
           to allocate the other_sids and gids structures has been deleted - so
279
 
           these pointers are no longer valid..... */
280
 
 
281
 
        cli_shutdown(cli);
282
 
        return nt_status;
283
 
}
284
 
 
285
 
/****************************************************************************
286
 
 Check for a valid username and password in security=domain mode.
287
 
****************************************************************************/
288
 
 
289
 
static NTSTATUS check_ntdomain_security(const struct auth_context *auth_context,
290
 
                                        void *my_private_data, 
291
 
                                        TALLOC_CTX *mem_ctx,
292
 
                                        const auth_usersupplied_info *user_info, 
293
 
                                        auth_serversupplied_info **server_info)
294
 
{
295
 
        NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
296
 
        const char *domain = lp_workgroup();
297
 
        fstring dc_name;
298
 
        struct in_addr dc_ip;
299
 
 
300
 
        if ( lp_server_role() != ROLE_DOMAIN_MEMBER ) {
301
 
                DEBUG(0,("check_ntdomain_security: Configuration error!  Cannot use "
302
 
                        "ntdomain auth method when not a member of a domain.\n"));
303
 
                return NT_STATUS_NOT_IMPLEMENTED;
304
 
        }
305
 
 
306
 
        if (!user_info || !server_info || !auth_context) {
307
 
                DEBUG(1,("check_ntdomain_security: Critical variables not present.  Failing.\n"));
308
 
                return NT_STATUS_INVALID_PARAMETER;
309
 
        }
310
 
 
311
 
        /* 
312
 
         * Check that the requested domain is not our own machine name.
313
 
         * If it is, we should never check the PDC here, we use our own local
314
 
         * password file.
315
 
         */
316
 
 
317
 
        if(strequal(get_global_sam_name(), user_info->domain)) {
318
 
                DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n"));
319
 
                return NT_STATUS_NOT_IMPLEMENTED;
320
 
        }
321
 
 
322
 
        /* we need our DC to send the net_sam_logon() request to */
323
 
 
324
 
        if ( !get_dc_name(domain, NULL, dc_name, &dc_ip) ) {
325
 
                DEBUG(5,("check_ntdomain_security: unable to locate a DC for domain %s\n",
326
 
                        user_info->domain));
327
 
                return NT_STATUS_NO_LOGON_SERVERS;
328
 
        }
329
 
        
330
 
        nt_status = domain_client_validate(mem_ctx,
331
 
                                        user_info,
332
 
                                        domain,
333
 
                                        (uchar *)auth_context->challenge.data,
334
 
                                        server_info,
335
 
                                        dc_name,
336
 
                                        dc_ip);
337
 
                
338
 
        return nt_status;
339
 
}
340
 
 
341
 
/* module initialisation */
342
 
static NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
343
 
{
344
 
        if (!make_auth_methods(auth_context, auth_method)) {
345
 
                return NT_STATUS_NO_MEMORY;
346
 
        }
347
 
 
348
 
        (*auth_method)->name = "ntdomain";
349
 
        (*auth_method)->auth = check_ntdomain_security;
350
 
        return NT_STATUS_OK;
351
 
}
352
 
 
353
 
 
354
 
/****************************************************************************
355
 
 Check for a valid username and password in a trusted domain
356
 
****************************************************************************/
357
 
 
358
 
static NTSTATUS check_trustdomain_security(const struct auth_context *auth_context,
359
 
                                           void *my_private_data, 
360
 
                                           TALLOC_CTX *mem_ctx,
361
 
                                           const auth_usersupplied_info *user_info, 
362
 
                                           auth_serversupplied_info **server_info)
363
 
{
364
 
        NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
365
 
        unsigned char trust_md4_password[16];
366
 
        char *trust_password;
367
 
        time_t last_change_time;
368
 
        DOM_SID sid;
369
 
        fstring dc_name;
370
 
        struct in_addr dc_ip;
371
 
 
372
 
        if (!user_info || !server_info || !auth_context) {
373
 
                DEBUG(1,("check_trustdomain_security: Critical variables not present.  Failing.\n"));
374
 
                return NT_STATUS_INVALID_PARAMETER;
375
 
        }
376
 
 
377
 
        /* 
378
 
         * Check that the requested domain is not our own machine name or domain name.
379
 
         */
380
 
 
381
 
        if( strequal(get_global_sam_name(), user_info->domain)) {
382
 
                DEBUG(3,("check_trustdomain_security: Requested domain [%s] was for this machine.\n",
383
 
                        user_info->domain));
384
 
                return NT_STATUS_NOT_IMPLEMENTED;
385
 
        }
386
 
 
387
 
        /* No point is bothering if this is not a trusted domain.
388
 
           This return makes "map to guest = bad user" work again.
389
 
           The logic is that if we know nothing about the domain, that
390
 
           user is not known to us and does not exist */
391
 
        
392
 
        if ( !is_trusted_domain( user_info->domain ) )
393
 
                return NT_STATUS_NOT_IMPLEMENTED;
394
 
 
395
 
        /*
396
 
         * Get the trusted account password for the trusted domain
397
 
         * No need to become_root() as secrets_init() is done at startup.
398
 
         */
399
 
 
400
 
        if (!secrets_fetch_trusted_domain_password(user_info->domain, &trust_password,
401
 
                                &sid, &last_change_time)) {
402
 
                DEBUG(0, ("check_trustdomain_security: could not fetch trust "
403
 
                          "account password for domain %s\n",
404
 
                          user_info->domain));
405
 
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
406
 
        }
407
 
 
408
 
#ifdef DEBUG_PASSWORD
409
 
        DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain,
410
 
                    trust_password));
411
 
#endif
412
 
        E_md4hash(trust_password, trust_md4_password);
413
 
        SAFE_FREE(trust_password);
414
 
 
415
 
#if 0
416
 
        /* Test if machine password is expired and need to be changed */
417
 
        if (time(NULL) > last_change_time + (time_t)lp_machine_password_timeout())
418
 
        {
419
 
                global_machine_password_needs_changing = True;
420
 
        }
421
 
#endif
422
 
 
423
 
        /* use get_dc_name() for consistency even through we know that it will be 
424
 
           a netbios name */
425
 
           
426
 
        if ( !get_dc_name(user_info->domain, NULL, dc_name, &dc_ip) ) {
427
 
                DEBUG(5,("check_trustdomain_security: unable to locate a DC for domain %s\n",
428
 
                        user_info->domain));
429
 
                return NT_STATUS_NO_LOGON_SERVERS;
430
 
        }
431
 
        
432
 
        nt_status = domain_client_validate(mem_ctx,
433
 
                                        user_info,
434
 
                                        user_info->domain,
435
 
                                        (uchar *)auth_context->challenge.data,
436
 
                                        server_info,
437
 
                                        dc_name,
438
 
                                        dc_ip);
439
 
 
440
 
        return nt_status;
441
 
}
442
 
 
443
 
/* module initialisation */
444
 
static NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
445
 
{
446
 
        if (!make_auth_methods(auth_context, auth_method)) {
447
 
                return NT_STATUS_NO_MEMORY;
448
 
        }
449
 
 
450
 
        (*auth_method)->name = "trustdomain";
451
 
        (*auth_method)->auth = check_trustdomain_security;
452
 
        return NT_STATUS_OK;
453
 
}
454
 
 
455
 
NTSTATUS auth_domain_init(void) 
456
 
{
457
 
        smb_register_auth(AUTH_INTERFACE_VERSION, "trustdomain", auth_init_trustdomain);
458
 
        smb_register_auth(AUTH_INTERFACE_VERSION, "ntdomain", auth_init_ntdomain);
459
 
        return NT_STATUS_OK;
460
 
}