import HEAD into svn+ssh://svn.samba.org/home/svn/samba/trunk
[metze/old/v3-2-winbind-ndr.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_m(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         conn->privs = NULL;
367
368         conn->read_only = lp_readonly(conn->service);
369         conn->admin_user = False;
370
371         /*
372          * If force user is true, then store the
373          * given userid and also the groups
374          * of the user we're forcing.
375          */
376         
377         if (*lp_force_user(snum)) {
378                 struct passwd *pass2;
379                 pstring fuser;
380                 pstrcpy(fuser,lp_force_user(snum));
381
382                 /* Allow %S to be used by force user. */
383                 pstring_sub(fuser,"%S",lp_servicename(snum));
384
385                 pass2 = (struct passwd *)Get_Pwnam(fuser);
386                 if (pass2) {
387                         conn->uid = pass2->pw_uid;
388                         conn->gid = pass2->pw_gid;
389                         string_set(&conn->user,pass2->pw_name);
390                         fstrcpy(user,pass2->pw_name);
391                         conn->force_user = True;
392                         DEBUG(3,("Forced user %s\n",user));       
393                 } else {
394                         DEBUG(1,("Couldn't find user %s\n",fuser));
395                         conn_free(conn);
396                         *status = NT_STATUS_NO_SUCH_USER;
397                         return NULL;
398                 }
399         }
400
401 #ifdef HAVE_GETGRNAM 
402         /*
403          * If force group is true, then override
404          * any groupid stored for the connecting user.
405          */
406         
407         if (*lp_force_group(snum)) {
408                 gid_t gid;
409                 pstring gname;
410                 pstring tmp_gname;
411                 BOOL user_must_be_member = False;
412                 
413                 pstrcpy(tmp_gname,lp_force_group(snum));
414                 
415                 if (tmp_gname[0] == '+') {
416                         user_must_be_member = True;
417                         /* even now, tmp_gname is null terminated */
418                         pstrcpy(gname,&tmp_gname[1]);
419                 } else {
420                         pstrcpy(gname,tmp_gname);
421                 }
422                 /* default service may be a group name          */
423                 pstring_sub(gname,"%S",lp_servicename(snum));
424                 gid = nametogid(gname);
425                 
426                 if (gid != (gid_t)-1) {
427
428                         /*
429                          * If the user has been forced and the forced group starts
430                          * with a '+', then we only set the group to be the forced
431                          * group if the forced user is a member of that group.
432                          * Otherwise, the meaning of the '+' would be ignored.
433                          */
434                         if (conn->force_user && user_must_be_member) {
435                                 if (user_in_group_list( user, gname, NULL, 0)) {
436                                                 conn->gid = gid;
437                                                 DEBUG(3,("Forced group %s for member %s\n",gname,user));
438                                 }
439                         } else {
440                                 conn->gid = gid;
441                                 DEBUG(3,("Forced group %s\n",gname));
442                         }
443                         conn->force_group = True;
444                 } else {
445                         DEBUG(1,("Couldn't find group %s\n",gname));
446                         conn_free(conn);
447                         *status = NT_STATUS_NO_SUCH_GROUP;
448                         return NULL;
449                 }
450         }
451 #endif /* HAVE_GETGRNAM */
452
453         {
454                 pstring s;
455                 pstrcpy(s,lp_pathname(snum));
456                 standard_sub_conn(conn,s,sizeof(s));
457                 string_set(&conn->connectpath,s);
458                 DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum)));
459         }
460
461         if (conn->force_user || conn->force_group) {
462
463                 /* groups stuff added by ih */
464                 conn->ngroups = 0;
465                 conn->groups = NULL;
466                 
467                 /* Find all the groups this uid is in and
468                    store them. Used by change_to_user() */
469                 initialise_groups(conn->user, conn->uid, conn->gid); 
470                 get_current_groups(conn->gid, &conn->ngroups,&conn->groups);
471                 
472                 conn->nt_user_token = create_nt_token(conn->uid, conn->gid, 
473                                                       conn->ngroups, conn->groups,
474                                                       guest);
475
476                 init_privilege(&(conn->privs));
477                 pdb_get_privilege_set(conn->nt_user_token->user_sids, conn->nt_user_token->num_sids, conn->privs);
478         }
479
480         /*
481          * New code to check if there's a share security descripter
482          * added from NT server manager. This is done after the
483          * smb.conf checks are done as we need a uid and token. JRA.
484          *
485          */
486
487         {
488                 BOOL can_write = share_access_check(conn, snum, vuser, FILE_WRITE_DATA);
489
490                 if (!can_write) {
491                         if (!share_access_check(conn, snum, vuser, FILE_READ_DATA)) {
492                                 /* No access, read or write. */
493                                 DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n",
494                                           lp_servicename(snum)));
495                                 conn_free(conn);
496                                 *status = NT_STATUS_ACCESS_DENIED;
497                                 return NULL;
498                         } else {
499                                 conn->read_only = True;
500                         }
501                 }
502         }
503         /* Initialise VFS function pointers */
504
505         if (!smbd_vfs_init(conn)) {
506                 DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn))));
507                 conn_free(conn);
508                 *status = NT_STATUS_BAD_NETWORK_NAME;
509                 return NULL;
510         }
511
512 /* ROOT Activities: */  
513         /* check number of connections */
514         if (!claim_connection(conn,
515                               lp_servicename(SNUM(conn)),
516                               lp_max_connections(SNUM(conn)),
517                               False,0)) {
518                 DEBUG(1,("too many connections - rejected\n"));
519                 conn_free(conn);
520                 *status = NT_STATUS_INSUFFICIENT_RESOURCES;
521                 return NULL;
522         }  
523
524         /* Preexecs are done here as they might make the dir we are to ChDir to below */
525         /* execute any "root preexec = " line */
526         if (*lp_rootpreexec(SNUM(conn))) {
527                 int ret;
528                 pstring cmd;
529                 pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
530                 standard_sub_conn(conn,cmd,sizeof(cmd));
531                 DEBUG(5,("cmd=%s\n",cmd));
532                 ret = smbrun(cmd,NULL);
533                 if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) {
534                         DEBUG(1,("root preexec gave %d - failing connection\n", ret));
535                         yield_connection(conn, lp_servicename(SNUM(conn)));
536                         conn_free(conn);
537                         *status = NT_STATUS_ACCESS_DENIED;
538                         return NULL;
539                 }
540         }
541
542 /* USER Activites: */
543         if (!change_to_user(conn, conn->vuid)) {
544                 /* No point continuing if they fail the basic checks */
545                 DEBUG(0,("Can't become connected user!\n"));
546                 conn_free(conn);
547                 *status = NT_STATUS_LOGON_FAILURE;
548                 return NULL;
549         }
550
551         /* Remember that a different vuid can connect later without these checks... */
552         
553         /* Preexecs are done here as they might make the dir we are to ChDir to below */
554         /* execute any "preexec = " line */
555         if (*lp_preexec(SNUM(conn))) {
556                 int ret;
557                 pstring cmd;
558                 pstrcpy(cmd,lp_preexec(SNUM(conn)));
559                 standard_sub_conn(conn,cmd,sizeof(cmd));
560                 ret = smbrun(cmd,NULL);
561                 if (ret != 0 && lp_preexec_close(SNUM(conn))) {
562                         DEBUG(1,("preexec gave %d - failing connection\n", ret));
563                         change_to_root_user();
564                         yield_connection(conn, lp_servicename(SNUM(conn)));
565                         conn_free(conn);
566                         *status = NT_STATUS_ACCESS_DENIED;
567                         return NULL;
568                 }
569         }
570
571 #ifdef WITH_FAKE_KASERVER
572         if (lp_afs_share(SNUM(conn))) {
573                 afs_login(conn);
574         }
575 #endif
576         
577 #if CHECK_PATH_ON_TCONX
578         /* win2000 does not check the permissions on the directory
579            during the tree connect, instead relying on permission
580            check during individual operations. To match this behaviour
581            I have disabled this chdir check (tridge) */
582         if (vfs_ChDir(conn,conn->connectpath) != 0) {
583                 DEBUG(0,("%s (%s) Can't change directory to %s (%s)\n",
584                          get_remote_machine_name(), conn->client_address,
585                          conn->connectpath,strerror(errno)));
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 #else
593         /* the alternative is just to check the directory exists */
594         if (stat(conn->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
595                 DEBUG(0,("'%s' does not exist or is not a directory, when connecting to [%s]\n", conn->connectpath, lp_servicename(SNUM(conn))));
596                 change_to_root_user();
597                 yield_connection(conn, lp_servicename(SNUM(conn)));
598                 conn_free(conn);
599                 *status = NT_STATUS_BAD_NETWORK_NAME;
600                 return NULL;
601         }
602 #endif
603         
604         string_set(&conn->origpath,conn->connectpath);
605         
606 #if SOFTLINK_OPTIMISATION
607         /* resolve any soft links early if possible */
608         if (vfs_ChDir(conn,conn->connectpath) == 0) {
609                 pstring s;
610                 pstrcpy(s,conn->connectpath);
611                 vfs_GetWd(conn,s);
612                 string_set(&conn->connectpath,s);
613                 vfs_ChDir(conn,conn->connectpath);
614         }
615 #endif
616         
617         /*
618          * Print out the 'connected as' stuff here as we need
619          * to know the effective uid and gid we will be using
620          * (at least initially).
621          */
622
623         if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
624                 dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address );
625                 dbgtext( "%s", srv_is_signing_active() ? "signed " : "");
626                 dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
627                 dbgtext( "initially as user %s ", user );
628                 dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() );
629                 dbgtext( "(pid %d)\n", (int)sys_getpid() );
630         }
631         
632         /* Add veto/hide lists */
633         if (!IS_IPC(conn) && !IS_PRINT(conn)) {
634                 set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn)));
635                 set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn)));
636                 set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn)));
637         }
638         
639         /* Invoke VFS make connection hook */
640
641         if (SMB_VFS_CONNECT(conn, lp_servicename(snum), user) < 0) {
642                 DEBUG(0,("make_connection: VFS make connection failed!\n"));
643                 change_to_root_user();
644                 conn_free(conn);
645                 *status = NT_STATUS_UNSUCCESSFUL;
646                 return NULL;
647         }
648
649         /* we've finished with the user stuff - go back to root */
650         change_to_root_user();
651             
652         return(conn);
653 }
654
655 /***************************************************************************************
656  Simple wrapper function for make_connection() to include a call to 
657  vfs_chdir()
658  **************************************************************************************/
659  
660 connection_struct *make_connection_with_chdir(const char *service_in, DATA_BLOB password, 
661                                    const char *dev, uint16 vuid, NTSTATUS *status)
662 {
663         connection_struct *conn = NULL;
664         
665         conn = make_connection(service_in, password, dev, vuid, status);
666         
667         /*
668          * make_connection() does not change the directory for us any more
669          * so we have to do it as a separate step  --jerry
670          */
671          
672         if ( conn && vfs_ChDir(conn,conn->connectpath) != 0 ) {
673                 DEBUG(0,("move_driver_to_download_area: Can't change directory to %s for [print$] (%s)\n",
674                          conn->connectpath,strerror(errno)));
675                 yield_connection(conn, lp_servicename(SNUM(conn)));
676                 conn_free(conn);
677                 *status = NT_STATUS_UNSUCCESSFUL;
678                 return NULL;
679         }
680         
681         return conn;
682 }
683
684 /****************************************************************************
685  Make a connection to a service.
686  *
687  * @param service 
688 ****************************************************************************/
689
690 connection_struct *make_connection(const char *service_in, DATA_BLOB password, 
691                                    const char *pdev, uint16 vuid, NTSTATUS *status)
692 {
693         uid_t euid;
694         user_struct *vuser = NULL;
695         fstring service;
696         fstring dev;
697         int snum = -1;
698
699         fstrcpy(dev, pdev);
700
701         /* This must ONLY BE CALLED AS ROOT. As it exits this function as root. */
702         if (!non_root_mode() && (euid = geteuid()) != 0) {
703                 DEBUG(0,("make_connection: PANIC ERROR. Called as nonroot (%u)\n", (unsigned int)euid ));
704                 smb_panic("make_connection: PANIC ERROR. Called as nonroot\n");
705         }
706
707         if(lp_security() != SEC_SHARE) {
708                 vuser = get_valid_user_struct(vuid);
709                 if (!vuser) {
710                         DEBUG(1,("make_connection: refusing to connect with no session setup\n"));
711                         *status = NT_STATUS_ACCESS_DENIED;
712                         return NULL;
713                 }
714         }
715
716         /* Logic to try and connect to the correct [homes] share, preferably without too many
717            getpwnam() lookups.  This is particulary nasty for winbind usernames, where the
718            share name isn't the same as unix username.
719
720            The snum of the homes share is stored on the vuser at session setup time.
721         */
722
723         if (strequal(service_in,HOMES_NAME)) {
724                 if(lp_security() != SEC_SHARE) {
725                         DATA_BLOB no_pw = data_blob(NULL, 0);
726                         if (vuser->homes_snum == -1) {
727                                 DEBUG(2, ("[homes] share not available for this user because it was not found or created at session setup time\n"));
728                                 *status = NT_STATUS_BAD_NETWORK_NAME;
729                                 return NULL;
730                         }
731                         DEBUG(5, ("making a connection to [homes] service created at session setup time\n"));
732                         return make_connection_snum(vuser->homes_snum,
733                                                     vuser, no_pw, 
734                                                     dev, status);
735                 } else {
736                         /* Security = share. Try with current_user_info.smb_name
737                          * as the username.  */
738                         if (*current_user_info.smb_name) {
739                                 fstring unix_username;
740                                 fstrcpy(unix_username,
741                                         current_user_info.smb_name);
742                                 map_username(unix_username);
743                                 snum = find_service(unix_username);
744                         } 
745                         if (snum != -1) {
746                                 DEBUG(5, ("making a connection to 'homes' service %s based on security=share\n", service_in));
747                                 return make_connection_snum(snum, NULL,
748                                                             password,
749                                                             dev, status);
750                         }
751                 }
752         } else if ((lp_security() != SEC_SHARE) && (vuser->homes_snum != -1)
753                    && strequal(service_in, lp_servicename(vuser->homes_snum))) {
754                 DATA_BLOB no_pw = data_blob(NULL, 0);
755                 DEBUG(5, ("making a connection to 'homes' service [%s] created at session setup time\n", service_in));
756                 return make_connection_snum(vuser->homes_snum,
757                                             vuser, no_pw, 
758                                             dev, status);
759         }
760         
761         fstrcpy(service, service_in);
762
763         strlower_m(service);
764
765         snum = find_service(service);
766
767         if (snum < 0) {
768                 if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) {
769                         DEBUG(3,("refusing IPC connection to %s\n", service));
770                         *status = NT_STATUS_ACCESS_DENIED;
771                         return NULL;
772                 }
773
774                 DEBUG(0,("%s (%s) couldn't find service %s\n",
775                          get_remote_machine_name(), client_addr(), service));
776                 *status = NT_STATUS_BAD_NETWORK_NAME;
777                 return NULL;
778         }
779
780         /* Handle non-Dfs clients attempting connections to msdfs proxy */
781         if (lp_host_msdfs() && (*lp_msdfs_proxy(snum) != '\0'))  {
782                 DEBUG(3, ("refusing connection to dfs proxy '%s'\n", service));
783                 *status = NT_STATUS_BAD_NETWORK_NAME;
784                 return NULL;
785         }
786
787         DEBUG(5, ("making a connection to 'normal' service %s\n", service));
788
789         return make_connection_snum(snum, vuser,
790                                     password,
791                                     dev, status);
792 }
793
794 /****************************************************************************
795 close a cnum
796 ****************************************************************************/
797 void close_cnum(connection_struct *conn, uint16 vuid)
798 {
799         DirCacheFlush(SNUM(conn));
800
801         change_to_root_user();
802
803         DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
804                                  get_remote_machine_name(),conn->client_address,
805                                  lp_servicename(SNUM(conn))));
806
807         /* Call VFS disconnect hook */    
808         SMB_VFS_DISCONNECT(conn);
809
810         yield_connection(conn, lp_servicename(SNUM(conn)));
811
812         file_close_conn(conn);
813         dptr_closecnum(conn);
814
815         /* make sure we leave the directory available for unmount */
816         vfs_ChDir(conn, "/");
817
818         /* execute any "postexec = " line */
819         if (*lp_postexec(SNUM(conn)) && 
820             change_to_user(conn, vuid))  {
821                 pstring cmd;
822                 pstrcpy(cmd,lp_postexec(SNUM(conn)));
823                 standard_sub_conn(conn,cmd,sizeof(cmd));
824                 smbrun(cmd,NULL);
825                 change_to_root_user();
826         }
827
828         change_to_root_user();
829         /* execute any "root postexec = " line */
830         if (*lp_rootpostexec(SNUM(conn)))  {
831                 pstring cmd;
832                 pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
833                 standard_sub_conn(conn,cmd,sizeof(cmd));
834                 smbrun(cmd,NULL);
835         }
836
837         conn_free(conn);
838 }