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