r570: Remove lots of globals to handle case issues - move them
[samba.git] / source / smbd / service.c
1 /* 
2    Unix SMB/CIFS implementation.
3    service (connection) opening and closing
4    Copyright (C) Andrew Tridgell 1992-1998
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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 extern struct timeval smb_last_time;
24 extern userdom_struct current_user_info;
25
26
27 /****************************************************************************
28  Load parameters specific to a connection/service.
29 ****************************************************************************/
30
31 BOOL set_current_service(connection_struct *conn,BOOL do_chdir)
32 {
33         extern char magic_char;
34         static connection_struct *last_conn;
35         int snum;
36
37         if (!conn)  {
38                 last_conn = NULL;
39                 return(False);
40         }
41
42         conn->lastused = smb_last_time.tv_sec;
43
44         snum = SNUM(conn);
45   
46         if (do_chdir &&
47             vfs_ChDir(conn,conn->connectpath) != 0 &&
48             vfs_ChDir(conn,conn->origpath) != 0) {
49                 DEBUG(0,("chdir (%s) failed\n",
50                          conn->connectpath));
51                 return(False);
52         }
53
54         if (conn == last_conn)
55                 return(True);
56
57         last_conn = conn;
58
59         magic_char = lp_magicchar(snum);
60         return(True);
61 }
62
63 /****************************************************************************
64  Add a home service. Returns the new service number or -1 if fail.
65 ****************************************************************************/
66
67 int add_home_service(const char *service, const char *username, const char *homedir)
68 {
69         int iHomeService;
70
71         if (!service || !homedir)
72                 return -1;
73
74         if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
75                 return -1;
76
77         /*
78          * If this is a winbindd provided username, remove
79          * the domain component before adding the service.
80          * Log a warning if the "path=" parameter does not
81          * include any macros.
82          */
83
84         {
85                 const char *p = strchr(service,*lp_winbind_separator());
86
87                 /* We only want the 'user' part of the string */
88                 if (p) {
89                         service = p + 1;
90                 }
91         }
92
93         if (!lp_add_home(service, iHomeService, username, homedir)) {
94                 return -1;
95         }
96         
97         return lp_servicenumber(service);
98
99 }
100
101
102 /**
103  * Find a service entry.
104  *
105  * @param service is modified (to canonical form??)
106  **/
107
108 int find_service(fstring service)
109 {
110         int iService;
111
112         all_string_sub(service,"\\","/",0);
113
114         iService = lp_servicenumber(service);
115
116         /* now handle the special case of a home directory */
117         if (iService < 0) {
118                 char *phome_dir = get_user_home_dir(service);
119
120                 if(!phome_dir) {
121                         /*
122                          * Try mapping the servicename, it may
123                          * be a Windows to unix mapped user name.
124                          */
125                         if(map_username(service))
126                                 phome_dir = get_user_home_dir(service);
127                 }
128
129                 DEBUG(3,("checking for home directory %s gave %s\n",service,
130                         phome_dir?phome_dir:"(NULL)"));
131
132                 iService = add_home_service(service,service /* 'username' */, phome_dir);
133         }
134
135         /* If we still don't have a service, attempt to add it as a printer. */
136         if (iService < 0) {
137                 int iPrinterService;
138
139                 if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) {
140                         char *pszTemp;
141
142                         DEBUG(3,("checking whether %s is a valid printer name...\n", service));
143                         pszTemp = lp_printcapname();
144                         if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp)) {
145                                 DEBUG(3,("%s is a valid printer name\n", service));
146                                 DEBUG(3,("adding %s as a printer service\n", service));
147                                 lp_add_printer(service, iPrinterService);
148                                 iService = lp_servicenumber(service);
149                                 if (iService < 0) {
150                                         DEBUG(0,("failed to add %s as a printer service!\n", service));
151                                 }
152                         } else {
153                                 DEBUG(3,("%s is not a valid printer name\n", service));
154                         }
155                 }
156         }
157
158         /* Check for default vfs service?  Unsure whether to implement this */
159         if (iService < 0) {
160         }
161
162         /* just possibly it's a default service? */
163         if (iService < 0) {
164                 char *pdefservice = lp_defaultservice();
165                 if (pdefservice && *pdefservice && !strequal(pdefservice,service) && !strstr_m(service,"..")) {
166                         /*
167                          * We need to do a local copy here as lp_defaultservice() 
168                          * returns one of the rotating lp_string buffers that
169                          * could get overwritten by the recursive find_service() call
170                          * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
171                          */
172                         pstring defservice;
173                         pstrcpy(defservice, pdefservice);
174                         iService = find_service(defservice);
175                         if (iService >= 0) {
176                                 all_string_sub(service, "_","/",0);
177                                 iService = lp_add_service(service, iService);
178                         }
179                 }
180         }
181
182         if (iService >= 0) {
183                 if (!VALID_SNUM(iService)) {
184                         DEBUG(0,("Invalid snum %d for %s\n",iService, service));
185                         iService = -1;
186                 }
187         }
188
189         if (iService < 0)
190                 DEBUG(3,("find_service() failed to find service %s\n", service));
191
192         return (iService);
193 }
194
195
196 /****************************************************************************
197  do some basic sainity checks on the share.  
198  This function modifies dev, ecode.
199 ****************************************************************************/
200
201 static NTSTATUS share_sanity_checks(int snum, fstring dev) 
202 {
203         
204         if (!lp_snum_ok(snum) || 
205             !check_access(smbd_server_fd(), 
206                           lp_hostsallow(snum), lp_hostsdeny(snum))) {    
207                 return NT_STATUS_ACCESS_DENIED;
208         }
209
210         if (dev[0] == '?' || !dev[0]) {
211                 if (lp_print_ok(snum)) {
212                         fstrcpy(dev,"LPT1:");
213                 } else if (strequal(lp_fstype(snum), "IPC")) {
214                         fstrcpy(dev, "IPC");
215                 } else {
216                         fstrcpy(dev,"A:");
217                 }
218         }
219
220         strupper_m(dev);
221
222         if (lp_print_ok(snum)) {
223                 if (!strequal(dev, "LPT1:")) {
224                         return NT_STATUS_BAD_DEVICE_TYPE;
225                 }
226         } else if (strequal(lp_fstype(snum), "IPC")) {
227                 if (!strequal(dev, "IPC")) {
228                         return NT_STATUS_BAD_DEVICE_TYPE;
229                 }
230         } else if (!strequal(dev, "A:")) {
231                 return NT_STATUS_BAD_DEVICE_TYPE;
232         }
233
234         /* Behave as a printer if we are supposed to */
235         if (lp_print_ok(snum) && (strcmp(dev, "A:") == 0)) {
236                 fstrcpy(dev, "LPT1:");
237         }
238
239         return NT_STATUS_OK;
240 }
241
242 /****************************************************************************
243   Make a connection, given the snum to connect to, and the vuser of the
244   connecting user if appropriate.
245 ****************************************************************************/
246
247 static connection_struct *make_connection_snum(int snum, user_struct *vuser,
248                                                DATA_BLOB password, 
249                                                const char *pdev, NTSTATUS *status)
250 {
251         struct passwd *pass = NULL;
252         BOOL guest = False;
253         connection_struct *conn;
254         struct stat st;
255         fstring user;
256         fstring dev;
257
258         *user = 0;
259         fstrcpy(dev, pdev);
260
261         if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, dev))) {
262                 return NULL;
263         }       
264
265         conn = conn_new();
266         if (!conn) {
267                 DEBUG(0,("Couldn't find free connection.\n"));
268                 *status = NT_STATUS_INSUFFICIENT_RESOURCES;
269                 return NULL;
270         }
271
272         if (lp_guest_only(snum)) {
273                 const char *guestname = lp_guestaccount();
274                 guest = True;
275                 pass = getpwnam_alloc(guestname);
276                 if (!pass) {
277                         DEBUG(0,("make_connection_snum: Invalid guest account %s??\n",guestname));
278                         conn_free(conn);
279                         *status = NT_STATUS_NO_SUCH_USER;
280                         return NULL;
281                 }
282                 fstrcpy(user,pass->pw_name);
283                 conn->force_user = True;
284                 conn->uid = pass->pw_uid;
285                 conn->gid = pass->pw_gid;
286                 string_set(&conn->user,pass->pw_name);
287                 passwd_free(&pass);
288                 DEBUG(3,("Guest only user %s\n",user));
289         } else if (vuser) {
290                 if (vuser->guest) {
291                         if (!lp_guest_ok(snum)) {
292                                 DEBUG(2, ("guest user (from session setup) not permitted to access this share (%s)\n", lp_servicename(snum)));
293                                       conn_free(conn);
294                                       *status = NT_STATUS_ACCESS_DENIED;
295                                       return NULL;
296                         }
297                 } else {
298                         if (!user_ok(vuser->user.unix_name, snum, vuser->groups, vuser->n_groups)) {
299                                 DEBUG(2, ("user '%s' (from session setup) not permitted to access this share (%s)\n", vuser->user.unix_name, lp_servicename(snum)));
300                                 conn_free(conn);
301                                 *status = NT_STATUS_ACCESS_DENIED;
302                                 return NULL;
303                         }
304                 }
305                 conn->vuid = vuser->vuid;
306                 conn->uid = vuser->uid;
307                 conn->gid = vuser->gid;
308                 string_set(&conn->user,vuser->user.unix_name);
309                 fstrcpy(user,vuser->user.unix_name);
310                 guest = vuser->guest; 
311         } else if (lp_security() == SEC_SHARE) {
312                 /* add it as a possible user name if we 
313                    are in share mode security */
314                 add_session_user(lp_servicename(snum));
315                 /* shall we let them in? */
316                 if (!authorise_login(snum,user,password,&guest)) {
317                         DEBUG( 2, ( "Invalid username/password for [%s]\n", 
318                                     lp_servicename(snum)) );
319                         conn_free(conn);
320                         *status = NT_STATUS_WRONG_PASSWORD;
321                         return NULL;
322                 }
323                 pass = Get_Pwnam(user);
324                 conn->force_user = True;
325                 conn->uid = pass->pw_uid;
326                 conn->gid = pass->pw_gid;
327                 string_set(&conn->user, pass->pw_name);
328                 fstrcpy(user, pass->pw_name);
329
330         } else {
331                 DEBUG(0, ("invalid VUID (vuser) but not in security=share\n"));
332                 conn_free(conn);
333                 *status = NT_STATUS_ACCESS_DENIED;
334                 return NULL;
335         }
336
337         add_session_user(user);
338
339         safe_strcpy(conn->client_address, client_addr(), 
340                     sizeof(conn->client_address)-1);
341         conn->num_files_open = 0;
342         conn->lastused = time(NULL);
343         conn->service = snum;
344         conn->used = True;
345         conn->printer = (strncmp(dev,"LPT",3) == 0);
346         conn->ipc = ((strncmp(dev,"IPC",3) == 0) || strequal(dev,"ADMIN$"));
347         conn->dirptr = NULL;
348
349         /* Case options for the share. */
350         conn->case_sensitive = lp_casesensitive(snum);
351         conn->case_preserve = lp_preservecase(snum);
352         conn->short_case_preserve = lp_shortpreservecase(snum);
353
354         conn->veto_list = NULL;
355         conn->hide_list = NULL;
356         conn->veto_oplock_list = NULL;
357         string_set(&conn->dirpath,"");
358         string_set(&conn->user,user);
359         conn->nt_user_token = NULL;
360
361         conn->read_only = lp_readonly(conn->service);
362         conn->admin_user = False;
363
364         /*
365          * If force user is true, then store the
366          * given userid and also the groups
367          * of the user we're forcing.
368          */
369         
370         if (*lp_force_user(snum)) {
371                 struct passwd *pass2;
372                 pstring fuser;
373                 pstrcpy(fuser,lp_force_user(snum));
374
375                 /* Allow %S to be used by force user. */
376                 pstring_sub(fuser,"%S",lp_servicename(snum));
377
378                 pass2 = (struct passwd *)Get_Pwnam(fuser);
379                 if (pass2) {
380                         conn->uid = pass2->pw_uid;
381                         conn->gid = pass2->pw_gid;
382                         string_set(&conn->user,pass2->pw_name);
383                         fstrcpy(user,pass2->pw_name);
384                         conn->force_user = True;
385                         DEBUG(3,("Forced user %s\n",user));       
386                 } else {
387                         DEBUG(1,("Couldn't find user %s\n",fuser));
388                         conn_free(conn);
389                         *status = NT_STATUS_NO_SUCH_USER;
390                         return NULL;
391                 }
392         }
393
394 #ifdef HAVE_GETGRNAM 
395         /*
396          * If force group is true, then override
397          * any groupid stored for the connecting user.
398          */
399         
400         if (*lp_force_group(snum)) {
401                 gid_t gid;
402                 pstring gname;
403                 pstring tmp_gname;
404                 BOOL user_must_be_member = False;
405                 
406                 pstrcpy(tmp_gname,lp_force_group(snum));
407                 
408                 if (tmp_gname[0] == '+') {
409                         user_must_be_member = True;
410                         /* even now, tmp_gname is null terminated */
411                         pstrcpy(gname,&tmp_gname[1]);
412                 } else {
413                         pstrcpy(gname,tmp_gname);
414                 }
415                 /* default service may be a group name          */
416                 pstring_sub(gname,"%S",lp_servicename(snum));
417                 gid = nametogid(gname);
418                 
419                 if (gid != (gid_t)-1) {
420
421                         /*
422                          * If the user has been forced and the forced group starts
423                          * with a '+', then we only set the group to be the forced
424                          * group if the forced user is a member of that group.
425                          * Otherwise, the meaning of the '+' would be ignored.
426                          */
427                         if (conn->force_user && user_must_be_member) {
428                                 if (user_in_group_list( user, gname, NULL, 0)) {
429                                                 conn->gid = gid;
430                                                 DEBUG(3,("Forced group %s for member %s\n",gname,user));
431                                 }
432                         } else {
433                                 conn->gid = gid;
434                                 DEBUG(3,("Forced group %s\n",gname));
435                         }
436                         conn->force_group = True;
437                 } else {
438                         DEBUG(1,("Couldn't find group %s\n",gname));
439                         conn_free(conn);
440                         *status = NT_STATUS_NO_SUCH_GROUP;
441                         return NULL;
442                 }
443         }
444 #endif /* HAVE_GETGRNAM */
445
446         {
447                 pstring s;
448                 pstrcpy(s,lp_pathname(snum));
449                 standard_sub_conn(conn,s,sizeof(s));
450                 string_set(&conn->connectpath,s);
451                 DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum)));
452         }
453
454         if (conn->force_user || conn->force_group) {
455
456                 /* groups stuff added by ih */
457                 conn->ngroups = 0;
458                 conn->groups = NULL;
459                 
460                 /* Find all the groups this uid is in and
461                    store them. Used by change_to_user() */
462                 initialise_groups(conn->user, conn->uid, conn->gid); 
463                 get_current_groups(conn->gid, &conn->ngroups,&conn->groups);
464                 
465                 conn->nt_user_token = create_nt_token(conn->uid, conn->gid, 
466                                                       conn->ngroups, conn->groups,
467                                                       guest);
468         }
469
470         /*
471          * New code to check if there's a share security descripter
472          * added from NT server manager. This is done after the
473          * smb.conf checks are done as we need a uid and token. JRA.
474          *
475          */
476
477         {
478                 BOOL can_write = share_access_check(conn, snum, vuser, FILE_WRITE_DATA);
479
480                 if (!can_write) {
481                         if (!share_access_check(conn, snum, vuser, FILE_READ_DATA)) {
482                                 /* No access, read or write. */
483                                 DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n",
484                                           lp_servicename(snum)));
485                                 conn_free(conn);
486                                 *status = NT_STATUS_ACCESS_DENIED;
487                                 return NULL;
488                         } else {
489                                 conn->read_only = True;
490                         }
491                 }
492         }
493         /* Initialise VFS function pointers */
494
495         if (!smbd_vfs_init(conn)) {
496                 DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn))));
497                 conn_free(conn);
498                 *status = NT_STATUS_BAD_NETWORK_NAME;
499                 return NULL;
500         }
501
502 /* ROOT Activities: */  
503         /* check number of connections */
504         if (!claim_connection(conn,
505                               lp_servicename(SNUM(conn)),
506                               lp_max_connections(SNUM(conn)),
507                               False,0)) {
508                 DEBUG(1,("too many connections - rejected\n"));
509                 conn_free(conn);
510                 *status = NT_STATUS_INSUFFICIENT_RESOURCES;
511                 return NULL;
512         }  
513
514         /* Preexecs are done here as they might make the dir we are to ChDir to below */
515         /* execute any "root preexec = " line */
516         if (*lp_rootpreexec(SNUM(conn))) {
517                 int ret;
518                 pstring cmd;
519                 pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
520                 standard_sub_conn(conn,cmd,sizeof(cmd));
521                 DEBUG(5,("cmd=%s\n",cmd));
522                 ret = smbrun(cmd,NULL);
523                 if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) {
524                         DEBUG(1,("root preexec gave %d - failing connection\n", ret));
525                         yield_connection(conn, lp_servicename(SNUM(conn)));
526                         conn_free(conn);
527                         *status = NT_STATUS_ACCESS_DENIED;
528                         return NULL;
529                 }
530         }
531
532 /* USER Activites: */
533         if (!change_to_user(conn, conn->vuid)) {
534                 /* No point continuing if they fail the basic checks */
535                 DEBUG(0,("Can't become connected user!\n"));
536                 conn_free(conn);
537                 *status = NT_STATUS_LOGON_FAILURE;
538                 return NULL;
539         }
540
541         /* Remember that a different vuid can connect later without these checks... */
542         
543         /* Preexecs are done here as they might make the dir we are to ChDir to below */
544         /* execute any "preexec = " line */
545         if (*lp_preexec(SNUM(conn))) {
546                 int ret;
547                 pstring cmd;
548                 pstrcpy(cmd,lp_preexec(SNUM(conn)));
549                 standard_sub_conn(conn,cmd,sizeof(cmd));
550                 ret = smbrun(cmd,NULL);
551                 if (ret != 0 && lp_preexec_close(SNUM(conn))) {
552                         DEBUG(1,("preexec gave %d - failing connection\n", ret));
553                         change_to_root_user();
554                         yield_connection(conn, lp_servicename(SNUM(conn)));
555                         conn_free(conn);
556                         *status = NT_STATUS_ACCESS_DENIED;
557                         return NULL;
558                 }
559         }
560
561 #ifdef WITH_FAKE_KASERVER
562         if (lp_afs_share(SNUM(conn))) {
563                 afs_login(conn);
564         }
565 #endif
566         
567 #if CHECK_PATH_ON_TCONX
568         /* win2000 does not check the permissions on the directory
569            during the tree connect, instead relying on permission
570            check during individual operations. To match this behaviour
571            I have disabled this chdir check (tridge) */
572         if (vfs_ChDir(conn,conn->connectpath) != 0) {
573                 DEBUG(0,("%s (%s) Can't change directory to %s (%s)\n",
574                          get_remote_machine_name(), conn->client_address,
575                          conn->connectpath,strerror(errno)));
576                 change_to_root_user();
577                 yield_connection(conn, lp_servicename(SNUM(conn)));
578                 conn_free(conn);
579                 *status = NT_STATUS_BAD_NETWORK_NAME;
580                 return NULL;
581         }
582 #else
583         /* the alternative is just to check the directory exists */
584         if (stat(conn->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
585                 DEBUG(0,("'%s' does not exist or is not a directory, when connecting to [%s]\n", conn->connectpath, lp_servicename(SNUM(conn))));
586                 change_to_root_user();
587                 yield_connection(conn, lp_servicename(SNUM(conn)));
588                 conn_free(conn);
589                 *status = NT_STATUS_BAD_NETWORK_NAME;
590                 return NULL;
591         }
592 #endif
593         
594         string_set(&conn->origpath,conn->connectpath);
595         
596 #if SOFTLINK_OPTIMISATION
597         /* resolve any soft links early if possible */
598         if (vfs_ChDir(conn,conn->connectpath) == 0) {
599                 pstring s;
600                 pstrcpy(s,conn->connectpath);
601                 vfs_GetWd(conn,s);
602                 string_set(&conn->connectpath,s);
603                 vfs_ChDir(conn,conn->connectpath);
604         }
605 #endif
606         
607         /*
608          * Print out the 'connected as' stuff here as we need
609          * to know the effective uid and gid we will be using
610          * (at least initially).
611          */
612
613         if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
614                 dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address );
615                 dbgtext( "%s", srv_is_signing_active() ? "signed " : "");
616                 dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
617                 dbgtext( "initially as user %s ", user );
618                 dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() );
619                 dbgtext( "(pid %d)\n", (int)sys_getpid() );
620         }
621         
622         /* Add veto/hide lists */
623         if (!IS_IPC(conn) && !IS_PRINT(conn)) {
624                 set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn)));
625                 set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn)));
626                 set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn)));
627         }
628         
629         /* Invoke VFS make connection hook */
630
631         if (SMB_VFS_CONNECT(conn, lp_servicename(snum), user) < 0) {
632                 DEBUG(0,("make_connection: VFS make connection failed!\n"));
633                 change_to_root_user();
634                 conn_free(conn);
635                 *status = NT_STATUS_UNSUCCESSFUL;
636                 return NULL;
637         }
638
639         /* we've finished with the user stuff - go back to root */
640         change_to_root_user();
641             
642         return(conn);
643 }
644
645 /***************************************************************************************
646  Simple wrapper function for make_connection() to include a call to 
647  vfs_chdir()
648  **************************************************************************************/
649  
650 connection_struct *make_connection_with_chdir(const char *service_in, DATA_BLOB password, 
651                                    const char *dev, uint16 vuid, NTSTATUS *status)
652 {
653         connection_struct *conn = NULL;
654         
655         conn = make_connection(service_in, password, dev, vuid, status);
656         
657         /*
658          * make_connection() does not change the directory for us any more
659          * so we have to do it as a separate step  --jerry
660          */
661          
662         if ( conn && vfs_ChDir(conn,conn->connectpath) != 0 ) {
663                 DEBUG(0,("move_driver_to_download_area: Can't change directory to %s for [print$] (%s)\n",
664                          conn->connectpath,strerror(errno)));
665                 yield_connection(conn, lp_servicename(SNUM(conn)));
666                 conn_free(conn);
667                 *status = NT_STATUS_UNSUCCESSFUL;
668                 return NULL;
669         }
670         
671         return conn;
672 }
673
674 /****************************************************************************
675  Make a connection to a service.
676  *
677  * @param service 
678 ****************************************************************************/
679
680 connection_struct *make_connection(const char *service_in, DATA_BLOB password, 
681                                    const char *pdev, uint16 vuid, NTSTATUS *status)
682 {
683         uid_t euid;
684         user_struct *vuser = NULL;
685         fstring service;
686         fstring dev;
687         int snum = -1;
688
689         fstrcpy(dev, pdev);
690
691         /* This must ONLY BE CALLED AS ROOT. As it exits this function as root. */
692         if (!non_root_mode() && (euid = geteuid()) != 0) {
693                 DEBUG(0,("make_connection: PANIC ERROR. Called as nonroot (%u)\n", (unsigned int)euid ));
694                 smb_panic("make_connection: PANIC ERROR. Called as nonroot\n");
695         }
696
697         if(lp_security() != SEC_SHARE) {
698                 vuser = get_valid_user_struct(vuid);
699                 if (!vuser) {
700                         DEBUG(1,("make_connection: refusing to connect with no session setup\n"));
701                         *status = NT_STATUS_ACCESS_DENIED;
702                         return NULL;
703                 }
704         }
705
706         /* Logic to try and connect to the correct [homes] share, preferably without too many
707            getpwnam() lookups.  This is particulary nasty for winbind usernames, where the
708            share name isn't the same as unix username.
709
710            The snum of the homes share is stored on the vuser at session setup time.
711         */
712
713         if (strequal(service_in,HOMES_NAME)) {
714                 if(lp_security() != SEC_SHARE) {
715                         DATA_BLOB no_pw = data_blob(NULL, 0);
716                         if (vuser->homes_snum == -1) {
717                                 DEBUG(2, ("[homes] share not available for this user because it was not found or created at session setup time\n"));
718                                 *status = NT_STATUS_BAD_NETWORK_NAME;
719                                 return NULL;
720                         }
721                         DEBUG(5, ("making a connection to [homes] service created at session setup time\n"));
722                         return make_connection_snum(vuser->homes_snum,
723                                                     vuser, no_pw, 
724                                                     dev, status);
725                 } else {
726                         /* Security = share. Try with current_user_info.smb_name
727                          * as the username.  */
728                         if (*current_user_info.smb_name) {
729                                 fstring unix_username;
730                                 fstrcpy(unix_username,
731                                         current_user_info.smb_name);
732                                 map_username(unix_username);
733                                 snum = find_service(unix_username);
734                         } 
735                         if (snum != -1) {
736                                 DEBUG(5, ("making a connection to 'homes' service %s based on security=share\n", service_in));
737                                 return make_connection_snum(snum, NULL,
738                                                             password,
739                                                             dev, status);
740                         }
741                 }
742         } else if ((lp_security() != SEC_SHARE) && (vuser->homes_snum != -1)
743                    && strequal(service_in, lp_servicename(vuser->homes_snum))) {
744                 DATA_BLOB no_pw = data_blob(NULL, 0);
745                 DEBUG(5, ("making a connection to 'homes' service [%s] created at session setup time\n", service_in));
746                 return make_connection_snum(vuser->homes_snum,
747                                             vuser, no_pw, 
748                                             dev, status);
749         }
750         
751         fstrcpy(service, service_in);
752
753         strlower_m(service);
754
755         snum = find_service(service);
756
757         if (snum < 0) {
758                 if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) {
759                         DEBUG(3,("refusing IPC connection to %s\n", service));
760                         *status = NT_STATUS_ACCESS_DENIED;
761                         return NULL;
762                 }
763
764                 DEBUG(0,("%s (%s) couldn't find service %s\n",
765                          get_remote_machine_name(), client_addr(), service));
766                 *status = NT_STATUS_BAD_NETWORK_NAME;
767                 return NULL;
768         }
769
770         /* Handle non-Dfs clients attempting connections to msdfs proxy */
771         if (lp_host_msdfs() && (*lp_msdfs_proxy(snum) != '\0'))  {
772                 DEBUG(3, ("refusing connection to dfs proxy '%s'\n", service));
773                 *status = NT_STATUS_BAD_NETWORK_NAME;
774                 return NULL;
775         }
776
777         DEBUG(5, ("making a connection to 'normal' service %s\n", service));
778
779         return make_connection_snum(snum, vuser,
780                                     password,
781                                     dev, status);
782 }
783
784 /****************************************************************************
785 close a cnum
786 ****************************************************************************/
787 void close_cnum(connection_struct *conn, uint16 vuid)
788 {
789         DirCacheFlush(SNUM(conn));
790
791         change_to_root_user();
792
793         DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
794                                  get_remote_machine_name(),conn->client_address,
795                                  lp_servicename(SNUM(conn))));
796
797         /* Call VFS disconnect hook */    
798         SMB_VFS_DISCONNECT(conn);
799
800         yield_connection(conn, lp_servicename(SNUM(conn)));
801
802         file_close_conn(conn);
803         dptr_closecnum(conn);
804
805         /* make sure we leave the directory available for unmount */
806         vfs_ChDir(conn, "/");
807
808         /* execute any "postexec = " line */
809         if (*lp_postexec(SNUM(conn)) && 
810             change_to_user(conn, vuid))  {
811                 pstring cmd;
812                 pstrcpy(cmd,lp_postexec(SNUM(conn)));
813                 standard_sub_conn(conn,cmd,sizeof(cmd));
814                 smbrun(cmd,NULL);
815                 change_to_root_user();
816         }
817
818         change_to_root_user();
819         /* execute any "root postexec = " line */
820         if (*lp_rootpostexec(SNUM(conn)))  {
821                 pstring cmd;
822                 pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
823                 standard_sub_conn(conn,cmd,sizeof(cmd));
824                 smbrun(cmd,NULL);
825         }
826
827         conn_free(conn);
828 }