amazing. the improvements to NT continue, evidence for which shows up
[tprouty/samba.git] / source / rpc_client / cli_netlogon.c
1 /* 
2  *  Unix SMB/Netbios implementation.
3  *  Version 1.9.
4  *  RPC Pipe client / server routines
5  *  Copyright (C) Andrew Tridgell              1992-1997,
6  *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
7  *  Copyright (C) Paul Ashton                       1997.
8  *  Copyright (C) Jeremy Allison                    1998.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *  
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *  
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25
26 #ifdef SYSLOG
27 #undef SYSLOG
28 #endif
29
30 #include "includes.h"
31
32 extern int DEBUGLEVEL;
33 extern pstring scope;
34 extern pstring global_myname;
35 extern fstring global_myworkgroup;
36
37 /****************************************************************************
38 Generate the next creds to use.
39 ****************************************************************************/
40
41 static void gen_next_creds( struct cli_state *cli, DOM_CRED *new_clnt_cred)
42 {
43   /*
44    * Create the new client credentials.
45    */
46
47   cli->clnt_cred.timestamp.time = time(NULL);
48
49   memcpy(new_clnt_cred, &cli->clnt_cred, sizeof(*new_clnt_cred));
50
51   /* Calculate the new credentials. */
52   cred_create(cli->sess_key, &(cli->clnt_cred.challenge),
53               new_clnt_cred->timestamp, &(new_clnt_cred->challenge));
54
55 }
56
57 #if UNUSED_CODE
58 /****************************************************************************
59 do a LSA Logon Control2
60 ****************************************************************************/
61 BOOL cli_net_logon_ctrl2(struct cli_state *cli, uint32 status_level)
62 {
63   prs_struct rbuf;
64   prs_struct buf; 
65   NET_Q_LOGON_CTRL2 q_l;
66   BOOL ok = False;
67
68   prs_init(&buf , 1024, 4, SAFETY_MARGIN, False);
69   prs_init(&rbuf, 0,    4, SAFETY_MARGIN, True );
70
71   /* create and send a MSRPC command with api NET_LOGON_CTRL2 */
72
73   DEBUG(4,("do_net_logon_ctrl2 from %s status level:%x\n",
74            global_myname, status_level));
75
76   /* store the parameters */
77   make_q_logon_ctrl2(&q_l, cli->srv_name_slash, status_level);
78
79   /* turn parameters into data stream */
80   net_io_q_logon_ctrl2("", &q_l,  &buf, 0);
81
82   /* send the data on \PIPE\ */
83   if (rpc_api_pipe_req(cli, NET_LOGON_CTRL2, &buf, &rbuf))
84   {
85     NET_R_LOGON_CTRL2 r_l;
86
87     net_io_r_logon_ctrl2("", &r_l, &rbuf, 0);
88     ok = (rbuf.offset != 0);
89                 
90     if (ok && r_l.status != 0)
91     {
92       /* report error code */
93       DEBUG(0,("do_net_logon_ctrl2: Error %s\n", get_nt_error_msg(r_l.status)));
94       cli->nt_error = r_l.status;
95       ok = False;
96     }
97   }
98
99   prs_mem_free(&rbuf);
100   prs_mem_free(&buf );
101
102   return ok;
103 }
104 #endif
105
106 /****************************************************************************
107 LSA Authenticate 2
108
109 Send the client credential, receive back a server credential.
110 Ensure that the server credential returned matches the session key 
111 encrypt of the server challenge originally received. JRA.
112 ****************************************************************************/
113
114 BOOL cli_net_auth2(struct cli_state *cli, uint16 sec_chan, 
115                    uint32 neg_flags, DOM_CHAL *srv_chal)
116 {
117   prs_struct rbuf;
118   prs_struct buf; 
119   NET_Q_AUTH_2 q_a;
120   BOOL ok = False;
121
122   prs_init(&buf , 1024, 4, SAFETY_MARGIN, False);
123   prs_init(&rbuf, 0,    4, SAFETY_MARGIN, True );
124
125   /* create and send a MSRPC command with api NET_AUTH2 */
126
127   DEBUG(4,("cli_net_auth2: srv:%s acct:%s sc:%x mc: %s chal %s neg: %x\n",
128          cli->srv_name_slash, cli->mach_acct, sec_chan, global_myname,
129          credstr(cli->clnt_cred.challenge.data), neg_flags));
130
131   /* store the parameters */
132   make_q_auth_2(&q_a, cli->srv_name_slash, cli->mach_acct, sec_chan, global_myname,
133                 &cli->clnt_cred.challenge, neg_flags);
134
135   /* turn parameters into data stream */
136   net_io_q_auth_2("", &q_a,  &buf, 0);
137
138   /* send the data on \PIPE\ */
139   if (rpc_api_pipe_req(cli, NET_AUTH2, &buf, &rbuf))
140   {
141     NET_R_AUTH_2 r_a;
142
143     net_io_r_auth_2("", &r_a, &rbuf, 0);
144     ok = (rbuf.offset != 0);
145                 
146     if (ok && r_a.status != 0)
147     {
148       /* report error code */
149       DEBUG(0,("cli_net_auth2: Error %s\n", get_nt_error_msg(r_a.status)));
150       cli->nt_error = r_a.status;
151       ok = False;
152     }
153
154     if (ok)
155     {
156       /* 
157        * Check the returned value using the initial
158        * server received challenge.
159        */
160       UTIME zerotime;
161
162       zerotime.time = 0;
163       if(cred_assert( &r_a.srv_chal, cli->sess_key, srv_chal, zerotime) == 0) {
164         /*
165          * Server replied with bad credential. Fail.
166          */
167         DEBUG(0,("cli_net_auth2: server %s replied with bad credential (bad machine \
168 password ?).\n", cli->desthost ));
169         ok = False;
170       }
171     }
172
173     if (ok && r_a.srv_flgs.neg_flags != q_a.clnt_flgs.neg_flags)
174     {
175       /* report different neg_flags */
176       DEBUG(0,("cli_net_auth2: error neg_flags (q,r) differ - (%x,%x)\n",
177           q_a.clnt_flgs.neg_flags, r_a.srv_flgs.neg_flags));
178       ok = False;
179     }
180
181   }
182
183   prs_mem_free(&rbuf);
184   prs_mem_free(&buf );
185
186   return ok;
187 }
188
189 /****************************************************************************
190 LSA Request Challenge. Sends our challenge to server, then gets
191 server response. These are used to generate the credentials.
192 ****************************************************************************/
193
194 BOOL cli_net_req_chal(struct cli_state *cli, DOM_CHAL *clnt_chal, DOM_CHAL *srv_chal)
195 {
196   prs_struct rbuf;
197   prs_struct buf; 
198   NET_Q_REQ_CHAL q_c;
199   BOOL valid_chal = False;
200
201   if (srv_chal == NULL || clnt_chal == NULL)
202     return False;
203
204   prs_init(&buf , 1024, 4, SAFETY_MARGIN, False);
205   prs_init(&rbuf, 0,    4, SAFETY_MARGIN, True );
206
207   /* create and send a MSRPC command with api NET_REQCHAL */
208
209   DEBUG(4,("cli_net_req_chal: LSA Request Challenge from %s to %s: %s\n",
210          cli->desthost, global_myname, credstr(clnt_chal->data)));
211
212   /* store the parameters */
213   make_q_req_chal(&q_c, cli->srv_name_slash, global_myname, clnt_chal);
214
215   /* turn parameters into data stream */
216   net_io_q_req_chal("", &q_c,  &buf, 0);
217
218   /* send the data on \PIPE\ */
219   if (rpc_api_pipe_req(cli, NET_REQCHAL, &buf, &rbuf))
220   {
221     NET_R_REQ_CHAL r_c;
222     BOOL ok;
223
224     net_io_r_req_chal("", &r_c, &rbuf, 0);
225     ok = (rbuf.offset != 0);
226                 
227     if (ok && r_c.status != 0)
228     {
229       /* report error code */
230       DEBUG(0,("cli_net_req_chal: Error %s\n", get_nt_error_msg(r_c.status)));
231       cli->nt_error = r_c.status;
232       ok = False;
233     }
234
235     if (ok)
236     {
237       /* ok, at last: we're happy. return the challenge */
238       memcpy(srv_chal, r_c.srv_chal.data, sizeof(srv_chal->data));
239       valid_chal = True;
240     }
241   }
242
243   prs_mem_free(&rbuf);
244   prs_mem_free(&buf );
245
246   return valid_chal;
247 }
248
249 /***************************************************************************
250 LSA Server Password Set.
251 ****************************************************************************/
252
253 BOOL cli_net_srv_pwset(struct cli_state *cli, uint8 hashed_mach_pwd[16])
254 {
255   prs_struct rbuf;
256   prs_struct buf; 
257   DOM_CRED new_clnt_cred;
258   NET_Q_SRV_PWSET q_s;
259   BOOL ok = False;
260   uint16 sec_chan_type = 2;
261
262   gen_next_creds( cli, &new_clnt_cred);
263
264   prs_init(&buf , 1024, 4, SAFETY_MARGIN, False);
265   prs_init(&rbuf, 0,    4, SAFETY_MARGIN, True );
266
267   /* create and send a MSRPC command with api NET_SRV_PWSET */
268
269   DEBUG(4,("cli_net_srv_pwset: srv:%s acct:%s sc: %d mc: %s clnt %s %x\n",
270            cli->srv_name_slash, cli->mach_acct, sec_chan_type, global_myname,
271            credstr(new_clnt_cred.challenge.data), new_clnt_cred.timestamp.time));
272
273   /* store the parameters */
274   make_q_srv_pwset(&q_s, cli->srv_name_slash, cli->mach_acct, sec_chan_type,
275                    global_myname, &new_clnt_cred, (char *)hashed_mach_pwd);
276
277   /* turn parameters into data stream */
278   net_io_q_srv_pwset("", &q_s,  &buf, 0);
279
280   /* send the data on \PIPE\ */
281   if (rpc_api_pipe_req(cli, NET_SRVPWSET, &buf, &rbuf))
282   {
283     NET_R_SRV_PWSET r_s;
284
285     net_io_r_srv_pwset("", &r_s, &rbuf, 0);
286     ok = (rbuf.offset != 0);
287                 
288     if (ok && r_s.status != 0)
289     {
290       /* report error code */
291       DEBUG(0,("cli_net_srv_pwset: %s\n", get_nt_error_msg(r_s.status)));
292       cli->nt_error = r_s.status;
293       ok = False;
294     }
295
296     /* Update the credentials. */
297     if (ok && !clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &(r_s.srv_cred)))
298     {
299       /*
300        * Server replied with bad credential. Fail.
301        */
302       DEBUG(0,("cli_net_srv_pwset: server %s replied with bad credential (bad machine \
303 password ?).\n", cli->desthost ));
304       ok = False;
305     }
306   }
307
308   prs_mem_free(&rbuf);
309   prs_mem_free(&buf );
310
311   return ok;
312 }
313
314 /***************************************************************************
315 LSA SAM Logon - interactive or network.
316 ****************************************************************************/
317
318 BOOL cli_net_sam_logon(struct cli_state *cli, NET_ID_INFO_CTR *ctr, 
319                        NET_USER_INFO_3 *user_info3)
320 {
321   DOM_CRED new_clnt_cred;
322   DOM_CRED dummy_rtn_creds;
323   prs_struct rbuf;
324   prs_struct buf; 
325   uint16 validation_level = 3;
326   NET_Q_SAM_LOGON q_s;
327   BOOL ok = False;
328
329   gen_next_creds( cli, &new_clnt_cred);
330
331   prs_init(&buf , 1024, 4, SAFETY_MARGIN, False);
332   prs_init(&rbuf, 0,    4, SAFETY_MARGIN, True );
333
334   /* create and send a MSRPC command with api NET_SAMLOGON */
335
336   DEBUG(4,("cli_net_sam_logon: srv:%s mc:%s clnt %s %x ll: %d\n",
337              cli->srv_name_slash, global_myname, 
338              credstr(new_clnt_cred.challenge.data), cli->clnt_cred.timestamp.time,
339              ctr->switch_value));
340
341   memset(&dummy_rtn_creds, '\0', sizeof(dummy_rtn_creds));
342         dummy_rtn_creds.timestamp.time = time(NULL);
343
344   /* store the parameters */
345   make_sam_info(&(q_s.sam_id), cli->srv_name_slash, global_myname,
346          &new_clnt_cred, &dummy_rtn_creds, ctr->switch_value, ctr, validation_level);
347
348   /* turn parameters into data stream */
349   net_io_q_sam_logon("", &q_s,  &buf, 0);
350
351   /* send the data on \PIPE\ */
352   if (rpc_api_pipe_req(cli, NET_SAMLOGON, &buf, &rbuf))
353   {
354     NET_R_SAM_LOGON r_s;
355
356     r_s.user = user_info3;
357
358     net_io_r_sam_logon("", &r_s, &rbuf, 0);
359     ok = (rbuf.offset != 0);
360                 
361     if (ok && r_s.status != 0)
362     {
363       /* report error code */
364       DEBUG(0,("cli_net_sam_logon: %s\n", get_nt_error_msg(r_s.status)));
365       cli->nt_error = r_s.status;
366       ok = False;
367     }
368
369     /* Update the credentials. */
370     if (ok && !clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &(r_s.srv_creds)))
371     {
372       /*
373        * Server replied with bad credential. Fail.
374        */
375       DEBUG(0,("cli_net_sam_logon: server %s replied with bad credential (bad machine \
376 password ?).\n", cli->desthost ));
377         ok = False;
378     }
379
380     if (ok && r_s.switch_value != 3)
381     {
382       /* report different switch_value */
383       DEBUG(0,("cli_net_sam_logon: switch_value of 3 expected %x\n",
384                    r_s.switch_value));
385       ok = False;
386     }
387   }
388
389   prs_mem_free(&rbuf);
390   prs_mem_free(&buf );
391
392   return ok;
393 }
394
395 /***************************************************************************
396 LSA SAM Logoff.
397
398 This currently doesnt work correctly as the domain controller 
399 returns NT_STATUS_INVALID_INFO_CLASS - we obviously need to
400 send a different info level. Right now though, I'm not sure
401 what that needs to be (I need to see one on the wire before
402 I can be sure). JRA.
403 ****************************************************************************/
404 BOOL cli_net_sam_logoff(struct cli_state *cli, NET_ID_INFO_CTR *ctr)
405 {
406   DOM_CRED new_clnt_cred;
407   DOM_CRED dummy_rtn_creds;
408   prs_struct rbuf;
409   prs_struct buf; 
410   NET_Q_SAM_LOGOFF q_s;
411   uint16 validation_level = 3;
412   BOOL ok = False;
413
414   gen_next_creds( cli, &new_clnt_cred);
415
416   prs_init(&buf , 1024, 4, SAFETY_MARGIN, False);
417   prs_init(&rbuf, 0,    4, SAFETY_MARGIN, True );
418
419   /* create and send a MSRPC command with api NET_SAMLOGOFF */
420
421   DEBUG(4,("cli_net_sam_logoff: srv:%s mc:%s clnt %s %x ll: %d\n",
422             cli->srv_name_slash, global_myname,
423             credstr(new_clnt_cred.challenge.data), new_clnt_cred.timestamp.time,
424             ctr->switch_value));
425
426   memset(&dummy_rtn_creds, '\0', sizeof(dummy_rtn_creds));
427
428   /* store the parameters */
429   make_sam_info(&(q_s.sam_id), cli->srv_name_slash, global_myname,
430                 &new_clnt_cred, &dummy_rtn_creds, ctr->switch_value, ctr, validation_level);
431
432   /* turn parameters into data stream */
433   net_io_q_sam_logoff("", &q_s,  &buf, 0);
434
435   /* send the data on \PIPE\ */
436   if (rpc_api_pipe_req(cli, NET_SAMLOGOFF, &buf, &rbuf))
437   {
438     NET_R_SAM_LOGOFF r_s;
439
440     net_io_r_sam_logoff("", &r_s, &rbuf, 0);
441     ok = (rbuf.offset != 0);
442                 
443     if (ok && r_s.status != 0)
444     {
445       /* report error code */
446       DEBUG(0,("cli_net_sam_logoff: %s\n", get_nt_error_msg(r_s.status)));
447       cli->nt_error = r_s.status;
448       ok = False;
449     }
450
451     /* Update the credentials. */
452     if (ok && !clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &(r_s.srv_creds)))
453     {
454       /*
455        * Server replied with bad credential. Fail.
456        */
457       DEBUG(0,("cli_net_sam_logoff: server %s replied with bad credential (bad machine \
458 password ?).\n", cli->desthost ));
459       ok = False;
460     }
461   }
462
463   prs_mem_free(&rbuf);
464   prs_mem_free(&buf );
465
466   return ok;
467 }
468
469 /*********************************************************
470  Change the domain password on the PDC.
471 **********************************************************/
472
473 static BOOL modify_trust_password( char *domain, char *remote_machine, 
474                           unsigned char orig_trust_passwd_hash[16],
475                           unsigned char new_trust_passwd_hash[16])
476 {
477   struct cli_state cli;
478   struct nmb_name calling, called;
479
480   ZERO_STRUCT(cli);
481   if(cli_initialise(&cli) == False) {
482     DEBUG(0,("modify_trust_password: unable to initialize client connection.\n"));
483     return False;
484   }
485
486   if(!resolve_name( remote_machine, &cli.dest_ip, 0x20)) {
487     DEBUG(0,("modify_trust_password: Can't resolve address for %s\n", remote_machine));
488     return False;
489   }
490
491   if (ismyip(cli.dest_ip)) {
492     DEBUG(0,("modify_trust_password: Machine %s is one of our addresses. Cannot add \
493 to ourselves.\n", remote_machine));
494     return False;
495   }
496
497   if (!cli_connect(&cli, remote_machine, &cli.dest_ip)) {
498     DEBUG(0,("modify_trust_password: unable to connect to SMB server on \
499 machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
500     return False;
501   }
502     
503   
504         make_nmb_name(&calling, global_myname , 0x0 , scope);
505         make_nmb_name(&called , remote_machine, 0x20, scope);
506
507         if (!cli_session_request(&cli, &calling, &called))
508         {
509     DEBUG(0,("modify_trust_password: machine %s rejected the session setup. \
510 Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
511     cli_shutdown(&cli);
512     return False;
513   }
514
515   cli.protocol = PROTOCOL_NT1;
516     
517   if (!cli_negprot(&cli)) {
518     DEBUG(0,("modify_trust_password: machine %s rejected the negotiate protocol. \
519 Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
520     cli_shutdown(&cli);
521     return False;
522   }
523   if (cli.protocol != PROTOCOL_NT1) {
524     DEBUG(0,("modify_trust_password: machine %s didn't negotiate NT protocol.\n", 
525             remote_machine));
526     cli_shutdown(&cli);
527     return False;
528   }
529     
530   /*
531    * Do an anonymous session setup.
532    */
533     
534   if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
535     DEBUG(0,("modify_trust_password: machine %s rejected the session setup. \
536 Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
537     cli_shutdown(&cli);
538     return False;
539   }
540     
541   if (!(cli.sec_mode & 1)) {
542     DEBUG(0,("modify_trust_password: machine %s isn't in user level security mode\n",
543           remote_machine));
544     cli_shutdown(&cli);
545     return False;
546   }
547     
548   if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
549     DEBUG(0,("modify_trust_password: machine %s rejected the tconX on the IPC$ share. \
550 Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
551     cli_shutdown(&cli);
552     return False;
553   }
554
555   /*
556    * Ok - we have an anonymous connection to the IPC$ share.
557    * Now start the NT Domain stuff :-).
558    */
559     
560   if(cli_nt_session_open(&cli, PIPE_NETLOGON) == False) {
561     DEBUG(0,("modify_trust_password: unable to open the domain client session to \
562 machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli)));
563     cli_nt_session_close(&cli);
564     cli_ulogoff(&cli);
565     cli_shutdown(&cli);
566     return False;
567   } 
568   
569   if(cli_nt_setup_creds(&cli, orig_trust_passwd_hash) == False) {
570     DEBUG(0,("modify_trust_password: unable to setup the PDC credentials to machine \
571 %s. Error was : %s.\n", remote_machine, cli_errstr(&cli)));
572     cli_nt_session_close(&cli);
573     cli_ulogoff(&cli);
574     cli_shutdown(&cli);
575     return False;
576   } 
577
578   if( cli_nt_srv_pwset( &cli,new_trust_passwd_hash ) == False) {
579     DEBUG(0,("modify_trust_password: unable to change password for machine %s in domain \
580 %s to Domain controller %s. Error was %s.\n", global_myname, domain, remote_machine, 
581                             cli_errstr(&cli)));
582     cli_close(&cli, cli.nt_pipe_fnum);
583     cli_ulogoff(&cli);
584     cli_shutdown(&cli);
585     return False;
586   }
587
588   cli_nt_session_close(&cli);
589   cli_ulogoff(&cli);
590   cli_shutdown(&cli);
591
592   return True;
593 }
594
595 /************************************************************************
596  Change the trust account password for a domain.
597  The user of this function must have locked the trust password file for
598  update.
599 ************************************************************************/
600
601 BOOL change_trust_account_password( char *domain, char *remote_machine_list)
602 {
603   fstring remote_machine;
604   unsigned char old_trust_passwd_hash[16];
605   unsigned char new_trust_passwd_hash[16];
606   time_t lct;
607   BOOL res;
608
609   if(!get_trust_account_password( old_trust_passwd_hash, &lct)) {
610     DEBUG(0,("change_trust_account_password: unable to read the machine \
611 account password for domain %s.\n", domain));
612     return False;
613   }
614
615   /*
616    * Create the new (random) password.
617    */
618   generate_random_buffer( new_trust_passwd_hash, 16, True);
619
620   while(remote_machine_list && 
621         next_token(&remote_machine_list, remote_machine, 
622                    LIST_SEP, sizeof(remote_machine))) {
623     strupper(remote_machine);
624     if(modify_trust_password( domain, remote_machine, 
625                               old_trust_passwd_hash, new_trust_passwd_hash)) {
626       DEBUG(0,("%s : change_trust_account_password: Changed password for \
627 domain %s.\n", timestring(), domain));
628       /*
629        * Return the result of trying to write the new password
630        * back into the trust account file.
631        */
632       res = set_trust_account_password(new_trust_passwd_hash);
633       memset(new_trust_passwd_hash, 0, 16);
634       memset(old_trust_passwd_hash, 0, 16);
635       return res;
636     }
637   }
638
639   memset(new_trust_passwd_hash, 0, 16);
640   memset(old_trust_passwd_hash, 0, 16);
641
642   DEBUG(0,("%s : change_trust_account_password: Failed to change password for \
643 domain %s.\n", timestring(), domain));
644   return False;
645 }