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