2 Unix SMB/CIFS implementation.
3 service (connection) opening and closing
4 Copyright (C) Andrew Tridgell 1992-1998
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.
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.
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.
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;
33 /****************************************************************************
34 Load parameters specific to a connection/service.
35 ****************************************************************************/
37 BOOL set_current_service(connection_struct *conn,BOOL do_chdir)
39 extern char magic_char;
40 static connection_struct *last_conn;
48 conn->lastused = smb_last_time.tv_sec;
53 vfs_ChDir(conn,conn->connectpath) != 0 &&
54 vfs_ChDir(conn,conn->origpath) != 0) {
55 DEBUG(0,("chdir (%s) failed\n",
60 if (conn == last_conn)
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);
75 /****************************************************************************
76 Add a home service. Returns the new service number or -1 if fail.
77 ****************************************************************************/
79 int add_home_service(const char *service, const char *username, const char *homedir)
83 if (!service || !homedir)
86 if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
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
97 const char *p = strchr(service,*lp_winbind_separator());
99 /* We only want the 'user' part of the string */
105 if (!lp_add_home(service, iHomeService, username, homedir)) {
109 return lp_servicenumber(service);
115 * Find a service entry. service is always in dos codepage.
117 * @param service is modified (to canonical form??)
119 int find_service(fstring service)
123 all_string_sub(service,"\\","/",0);
125 iService = lp_servicenumber(service);
127 /* now handle the special case of a home directory */
130 char *phome_dir = get_user_home_dir(service);
135 * Try mapping the servicename, it may
136 * be a Windows to unix mapped user name.
138 if(map_username(service))
139 phome_dir = get_user_home_dir(service);
142 DEBUG(3,("checking for home directory %s gave %s\n",service,
143 phome_dir?phome_dir:"(NULL)"));
145 iService = add_home_service(service,service /* 'username' */, phome_dir);
148 /* If we still don't have a service, attempt to add it as a printer. */
153 if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0)
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))
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);
166 DEBUG(0,("failed to add %s as a printer service!\n", service));
169 DEBUG(3,("%s is not a valid printer name\n", service));
173 /* Check for default vfs service? Unsure whether to implement this */
178 /* just possibly it's a default service? */
181 char *pdefservice = lp_defaultservice();
182 if (pdefservice && *pdefservice &&
183 !strequal(pdefservice,service) &&
184 !strstr(service,".."))
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>.
193 pstrcpy(defservice, pdefservice);
194 iService = find_service(defservice);
197 all_string_sub(service, "_","/",0);
198 iService = lp_add_service(service, iService);
204 if (!VALID_SNUM(iService))
206 DEBUG(0,("Invalid snum %d for %s\n",iService, service));
211 DEBUG(3,("find_service() failed to find service %s\n", service));
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)
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;
230 if (dev[0] == '?' || !dev[0]) {
231 if (lp_print_ok(snum)) {
232 fstrcpy(dev,"LPT1:");
233 } else if (strequal(lp_fstype(snum), "IPC")) {
242 if (lp_print_ok(snum)) {
243 if (!strequal(dev, "LPT1:")) {
244 return NT_STATUS_BAD_DEVICE_TYPE;
246 } else if (strequal(lp_fstype(snum), "IPC")) {
247 if (!strequal(dev, "IPC")) {
248 return NT_STATUS_BAD_DEVICE_TYPE;
250 } else if (!strequal(dev, "A:")) {
251 return NT_STATUS_BAD_DEVICE_TYPE;
254 /* Behave as a printer if we are supposed to */
255 if (lp_print_ok(snum) && (strcmp(dev, "A:") == 0)) {
256 fstrcpy(dev, "LPT1:");
262 /****************************************************************************
263 Make a connection, given the snum to connect to, and the vuser of the
264 connecting user if appropriate.
265 ****************************************************************************/
267 static connection_struct *make_connection_snum(int snum, user_struct *vuser,
269 const char *pdev, NTSTATUS *status)
271 struct passwd *pass = NULL;
273 connection_struct *conn;
281 if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, dev))) {
287 DEBUG(0,("Couldn't find free connection.\n"));
288 *status = NT_STATUS_INSUFFICIENT_RESOURCES;
292 if (lp_guest_only(snum)) {
293 const char *guestname = lp_guestaccount();
295 pass = getpwnam_alloc(guestname);
297 DEBUG(0,("make_connection_snum: Invalid guest account %s??\n",guestname));
299 *status = NT_STATUS_NO_SUCH_USER;
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);
308 DEBUG(3,("Guest only user %s\n",user));
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)));
314 *status = NT_STATUS_ACCESS_DENIED;
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)));
321 *status = NT_STATUS_ACCESS_DENIED;
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)) );
340 *status = NT_STATUS_WRONG_PASSWORD;
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);
351 DEBUG(0, ("invalid VUID (vuser) but not in security=share\n"));
353 *status = NT_STATUS_ACCESS_DENIED;
357 add_session_user(user);
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;
365 conn->printer = (strncmp(dev,"LPT",3) == 0);
366 conn->ipc = ((strncmp(dev,"IPC",3) == 0) || strequal(dev,"ADMIN$"));
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;
376 conn->read_only = lp_readonly(conn->service);
377 conn->admin_user = False;
380 * If force user is true, then store the
381 * given userid and also the groups
382 * of the user we're forcing.
385 if (*lp_force_user(snum)) {
386 struct passwd *pass2;
388 pstrcpy(fuser,lp_force_user(snum));
390 /* Allow %S to be used by force user. */
391 pstring_sub(fuser,"%S",lp_servicename(snum));
393 pass2 = (struct passwd *)Get_Pwnam(fuser);
395 conn->uid = pass2->pw_uid;
396 conn->gid = pass2->pw_gid;
397 string_set(&conn->user,pass2->pw_name);
398 fstrcpy(user,pass2->pw_name);
399 conn->force_user = True;
400 DEBUG(3,("Forced user %s\n",user));
402 DEBUG(1,("Couldn't find user %s\n",fuser));
404 *status = NT_STATUS_NO_SUCH_USER;
411 * If force group is true, then override
412 * any groupid stored for the connecting user.
415 if (*lp_force_group(snum)) {
419 BOOL user_must_be_member = False;
421 pstrcpy(tmp_gname,lp_force_group(snum));
423 if (tmp_gname[0] == '+') {
424 user_must_be_member = True;
425 /* even now, tmp_gname is null terminated */
426 pstrcpy(gname,&tmp_gname[1]);
428 pstrcpy(gname,tmp_gname);
430 /* default service may be a group name */
431 pstring_sub(gname,"%S",lp_servicename(snum));
432 gid = nametogid(gname);
434 if (gid != (gid_t)-1) {
437 * If the user has been forced and the forced group starts
438 * with a '+', then we only set the group to be the forced
439 * group if the forced user is a member of that group.
440 * Otherwise, the meaning of the '+' would be ignored.
442 if (conn->force_user && user_must_be_member) {
443 if (user_in_group_list( user, gname, NULL, 0)) {
445 DEBUG(3,("Forced group %s for member %s\n",gname,user));
449 DEBUG(3,("Forced group %s\n",gname));
451 conn->force_group = True;
453 DEBUG(1,("Couldn't find group %s\n",gname));
455 *status = NT_STATUS_NO_SUCH_GROUP;
459 #endif /* HAVE_GETGRNAM */
463 pstrcpy(s,lp_pathname(snum));
464 standard_sub_conn(conn,s,sizeof(s));
465 string_set(&conn->connectpath,s);
466 DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum)));
469 if (conn->force_user || conn->force_group) {
471 /* groups stuff added by ih */
475 /* Find all the groups this uid is in and
476 store them. Used by change_to_user() */
477 initialise_groups(conn->user, conn->uid, conn->gid);
478 get_current_groups(conn->gid, &conn->ngroups,&conn->groups);
480 conn->nt_user_token = create_nt_token(conn->uid, conn->gid,
481 conn->ngroups, conn->groups,
484 init_privilege(&(conn->privs));
485 pdb_get_privilege_set(conn->nt_user_token, conn->privs);
489 * New code to check if there's a share security descripter
490 * added from NT server manager. This is done after the
491 * smb.conf checks are done as we need a uid and token. JRA.
496 BOOL can_write = share_access_check(conn, snum, vuser, FILE_WRITE_DATA);
499 if (!share_access_check(conn, snum, vuser, FILE_READ_DATA)) {
500 /* No access, read or write. */
501 DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n",
502 lp_servicename(snum)));
504 *status = NT_STATUS_ACCESS_DENIED;
507 conn->read_only = True;
511 /* Initialise VFS function pointers */
513 if (!smbd_vfs_init(conn)) {
514 DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn))));
516 *status = NT_STATUS_BAD_NETWORK_NAME;
520 /* ROOT Activities: */
521 /* check number of connections */
522 if (!claim_connection(conn,
523 lp_servicename(SNUM(conn)),
524 lp_max_connections(SNUM(conn)),
526 DEBUG(1,("too many connections - rejected\n"));
528 *status = NT_STATUS_INSUFFICIENT_RESOURCES;
532 /* Preexecs are done here as they might make the dir we are to ChDir to below */
533 /* execute any "root preexec = " line */
534 if (*lp_rootpreexec(SNUM(conn))) {
537 pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
538 standard_sub_conn(conn,cmd,sizeof(cmd));
539 DEBUG(5,("cmd=%s\n",cmd));
540 ret = smbrun(cmd,NULL);
541 if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) {
542 DEBUG(1,("root preexec gave %d - failing connection\n", ret));
543 yield_connection(conn, lp_servicename(SNUM(conn)));
545 *status = NT_STATUS_ACCESS_DENIED;
550 /* USER Activites: */
551 if (!change_to_user(conn, conn->vuid)) {
552 /* No point continuing if they fail the basic checks */
553 DEBUG(0,("Can't become connected user!\n"));
555 *status = NT_STATUS_LOGON_FAILURE;
559 /* Remember that a different vuid can connect later without these checks... */
561 /* Preexecs are done here as they might make the dir we are to ChDir to below */
562 /* execute any "preexec = " line */
563 if (*lp_preexec(SNUM(conn))) {
566 pstrcpy(cmd,lp_preexec(SNUM(conn)));
567 standard_sub_conn(conn,cmd,sizeof(cmd));
568 ret = smbrun(cmd,NULL);
569 if (ret != 0 && lp_preexec_close(SNUM(conn))) {
570 DEBUG(1,("preexec gave %d - failing connection\n", ret));
571 change_to_root_user();
572 yield_connection(conn, lp_servicename(SNUM(conn)));
574 *status = NT_STATUS_ACCESS_DENIED;
579 #ifdef WITH_FAKE_KASERVER
580 if (lp_afs_share(SNUM(conn))) {
585 #if CHECK_PATH_ON_TCONX
586 /* win2000 does not check the permissions on the directory
587 during the tree connect, instead relying on permission
588 check during individual operations. To match this behaviour
589 I have disabled this chdir check (tridge) */
590 if (vfs_ChDir(conn,conn->connectpath) != 0) {
591 DEBUG(0,("%s (%s) Can't change directory to %s (%s)\n",
592 get_remote_machine_name(), conn->client_address,
593 conn->connectpath,strerror(errno)));
594 change_to_root_user();
595 yield_connection(conn, lp_servicename(SNUM(conn)));
597 *status = NT_STATUS_BAD_NETWORK_NAME;
601 /* the alternative is just to check the directory exists */
602 if (stat(conn->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
603 DEBUG(0,("'%s' does not exist or is not a directory, when connecting to [%s]\n", conn->connectpath, lp_servicename(SNUM(conn))));
604 change_to_root_user();
605 yield_connection(conn, lp_servicename(SNUM(conn)));
607 *status = NT_STATUS_BAD_NETWORK_NAME;
612 string_set(&conn->origpath,conn->connectpath);
614 #if SOFTLINK_OPTIMISATION
615 /* resolve any soft links early if possible */
616 if (vfs_ChDir(conn,conn->connectpath) == 0) {
618 pstrcpy(s,conn->connectpath);
620 string_set(&conn->connectpath,s);
621 vfs_ChDir(conn,conn->connectpath);
626 * Print out the 'connected as' stuff here as we need
627 * to know the effective uid and gid we will be using
628 * (at least initially).
631 if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
632 dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address );
633 dbgtext( "%s", srv_is_signing_active() ? "signed " : "");
634 dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
635 dbgtext( "initially as user %s ", user );
636 dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() );
637 dbgtext( "(pid %d)\n", (int)sys_getpid() );
640 /* Add veto/hide lists */
641 if (!IS_IPC(conn) && !IS_PRINT(conn)) {
642 set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn)));
643 set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn)));
644 set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn)));
647 /* Invoke VFS make connection hook */
649 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), user) < 0) {
650 DEBUG(0,("make_connection: VFS make connection failed!\n"));
651 change_to_root_user();
653 *status = NT_STATUS_UNSUCCESSFUL;
657 /* we've finished with the user stuff - go back to root */
658 change_to_root_user();
663 /***************************************************************************************
664 Simple wrapper function for make_connection() to include a call to
666 **************************************************************************************/
668 connection_struct *make_connection_with_chdir(const char *service_in, DATA_BLOB password,
669 const char *dev, uint16 vuid, NTSTATUS *status)
671 connection_struct *conn = NULL;
673 conn = make_connection(service_in, password, dev, vuid, status);
676 * make_connection() does not change the directory for us any more
677 * so we have to do it as a separate step --jerry
680 if ( conn && vfs_ChDir(conn,conn->connectpath) != 0 ) {
681 DEBUG(0,("move_driver_to_download_area: Can't change directory to %s for [print$] (%s)\n",
682 conn->connectpath,strerror(errno)));
683 yield_connection(conn, lp_servicename(SNUM(conn)));
685 *status = NT_STATUS_UNSUCCESSFUL;
692 /****************************************************************************
693 Make a connection to a service.
696 ****************************************************************************/
698 connection_struct *make_connection(const char *service_in, DATA_BLOB password,
699 const char *pdev, uint16 vuid, NTSTATUS *status)
702 user_struct *vuser = NULL;
709 /* This must ONLY BE CALLED AS ROOT. As it exits this function as root. */
710 if (!non_root_mode() && (euid = geteuid()) != 0) {
711 DEBUG(0,("make_connection: PANIC ERROR. Called as nonroot (%u)\n", (unsigned int)euid ));
712 smb_panic("make_connection: PANIC ERROR. Called as nonroot\n");
715 if(lp_security() != SEC_SHARE) {
716 vuser = get_valid_user_struct(vuid);
718 DEBUG(1,("make_connection: refusing to connect with no session setup\n"));
719 *status = NT_STATUS_ACCESS_DENIED;
724 /* Logic to try and connect to the correct [homes] share, preferably without too many
725 getpwnam() lookups. This is particulary nasty for winbind usernames, where the
726 share name isn't the same as unix username.
728 The snum of the homes share is stored on the vuser at session setup time.
731 if (strequal(service_in,HOMES_NAME)) {
732 if(lp_security() != SEC_SHARE) {
733 DATA_BLOB no_pw = data_blob(NULL, 0);
734 if (vuser->homes_snum == -1) {
735 DEBUG(2, ("[homes] share not available for this user because it was not found or created at session setup time\n"));
736 *status = NT_STATUS_BAD_NETWORK_NAME;
739 DEBUG(5, ("making a connection to [homes] service created at session setup time\n"));
740 return make_connection_snum(vuser->homes_snum,
744 /* Security = share. Try with current_user_info.smb_name
745 * as the username. */
746 if (*current_user_info.smb_name) {
747 fstring unix_username;
748 fstrcpy(unix_username,
749 current_user_info.smb_name);
750 map_username(unix_username);
751 snum = find_service(unix_username);
754 DEBUG(5, ("making a connection to 'homes' service %s based on security=share\n", service_in));
755 return make_connection_snum(snum, NULL,
760 } else if ((lp_security() != SEC_SHARE) && (vuser->homes_snum != -1)
761 && strequal(service_in, lp_servicename(vuser->homes_snum))) {
762 DATA_BLOB no_pw = data_blob(NULL, 0);
763 DEBUG(5, ("making a connection to 'homes' service [%s] created at session setup time\n", service_in));
764 return make_connection_snum(vuser->homes_snum,
769 fstrcpy(service, service_in);
773 snum = find_service(service);
776 if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) {
777 DEBUG(3,("refusing IPC connection to %s\n", service));
778 *status = NT_STATUS_ACCESS_DENIED;
782 DEBUG(0,("%s (%s) couldn't find service %s\n",
783 get_remote_machine_name(), client_addr(), service));
784 *status = NT_STATUS_BAD_NETWORK_NAME;
788 /* Handle non-Dfs clients attempting connections to msdfs proxy */
789 if (lp_host_msdfs() && (*lp_msdfs_proxy(snum) != '\0')) {
790 DEBUG(3, ("refusing connection to dfs proxy '%s'\n", service));
791 *status = NT_STATUS_BAD_NETWORK_NAME;
795 DEBUG(5, ("making a connection to 'normal' service %s\n", service));
797 return make_connection_snum(snum, vuser,
802 /****************************************************************************
804 ****************************************************************************/
805 void close_cnum(connection_struct *conn, uint16 vuid)
807 DirCacheFlush(SNUM(conn));
809 change_to_root_user();
811 DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
812 get_remote_machine_name(),conn->client_address,
813 lp_servicename(SNUM(conn))));
815 /* Call VFS disconnect hook */
816 SMB_VFS_DISCONNECT(conn);
818 yield_connection(conn, lp_servicename(SNUM(conn)));
820 file_close_conn(conn);
821 dptr_closecnum(conn);
823 /* make sure we leave the directory available for unmount */
824 vfs_ChDir(conn, "/");
826 /* execute any "postexec = " line */
827 if (*lp_postexec(SNUM(conn)) &&
828 change_to_user(conn, vuid)) {
830 pstrcpy(cmd,lp_postexec(SNUM(conn)));
831 standard_sub_conn(conn,cmd,sizeof(cmd));
833 change_to_root_user();
836 change_to_root_user();
837 /* execute any "root postexec = " line */
838 if (*lp_rootpostexec(SNUM(conn))) {
840 pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
841 standard_sub_conn(conn,cmd,sizeof(cmd));