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