diff options
Diffstat (limited to 'source3/smbd/service.c')
-rw-r--r-- | source3/smbd/service.c | 542 |
1 files changed, 542 insertions, 0 deletions
diff --git a/source3/smbd/service.c b/source3/smbd/service.c new file mode 100644 index 0000000000..b0c74aa53e --- /dev/null +++ b/source3/smbd/service.c @@ -0,0 +1,542 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + service (connection) opening and closing + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern time_t smb_last_time; +extern int case_default; +extern BOOL case_preserve; +extern BOOL short_case_preserve; +extern BOOL case_mangle; +extern BOOL case_sensitive; +extern BOOL use_mangled_map; +extern fstring remote_machine; +extern pstring sesssetup_user; +extern fstring remote_machine; + + +/**************************************************************************** +load parameters specific to a connection/service +****************************************************************************/ +BOOL become_service(connection_struct *conn,BOOL do_chdir) +{ + extern char magic_char; + static connection_struct *last_conn; + int snum; + + if (!conn) { + last_conn = NULL; + return(False); + } + + conn->lastused = smb_last_time; + + snum = SNUM(conn); + + if (do_chdir && + ChDir(conn->connectpath) != 0 && + ChDir(conn->origpath) != 0) { + DEBUG(0,("chdir (%s) failed\n", + conn->connectpath)); + return(False); + } + + if (conn == last_conn) + return(True); + + last_conn = conn; + + case_default = lp_defaultcase(snum); + case_preserve = lp_preservecase(snum); + short_case_preserve = lp_shortpreservecase(snum); + case_mangle = lp_casemangle(snum); + case_sensitive = lp_casesensitive(snum); + magic_char = lp_magicchar(snum); + use_mangled_map = (*lp_mangled_map(snum) ? True:False); + return(True); +} + + +/**************************************************************************** + find a service entry +****************************************************************************/ +int find_service(char *service) +{ + int iService; + + string_sub(service,"\\","/"); + + iService = lp_servicenumber(service); + + /* now handle the special case of a home directory */ + if (iService < 0) + { + char *phome_dir = get_home_dir(service); + + if(!phome_dir) + { + /* + * Try mapping the servicename, it may + * be a Windows to unix mapped user name. + */ + if(map_username(service)) + phome_dir = get_home_dir(service); + } + + DEBUG(3,("checking for home directory %s gave %s\n",service, + phome_dir?phome_dir:"(NULL)")); + + if (phome_dir) + { + int iHomeService; + if ((iHomeService = lp_servicenumber(HOMES_NAME)) >= 0) + { + lp_add_home(service,iHomeService,phome_dir); + iService = lp_servicenumber(service); + } + } + } + + /* If we still don't have a service, attempt to add it as a printer. */ + if (iService < 0) + { + int iPrinterService; + + if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) + { + char *pszTemp; + + DEBUG(3,("checking whether %s is a valid printer name...\n", service)); + pszTemp = PRINTCAP; + if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp)) + { + DEBUG(3,("%s is a valid printer name\n", service)); + DEBUG(3,("adding %s as a printer service\n", service)); + lp_add_printer(service,iPrinterService); + iService = lp_servicenumber(service); + if (iService < 0) + DEBUG(0,("failed to add %s as a printer service!\n", service)); + } + else + DEBUG(3,("%s is not a valid printer name\n", service)); + } + } + + /* just possibly it's a default service? */ + if (iService < 0) + { + char *pdefservice = lp_defaultservice(); + if (pdefservice && *pdefservice && !strequal(pdefservice,service)) + { + /* + * We need to do a local copy here as lp_defaultservice() + * returns one of the rotating lp_string buffers that + * could get overwritten by the recursive find_service() call + * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>. + */ + pstring defservice; + pstrcpy(defservice, pdefservice); + iService = find_service(defservice); + if (iService >= 0) + { + string_sub(service,"_","/"); + iService = lp_add_service(service,iService); + } + } + } + + if (iService >= 0) + if (!VALID_SNUM(iService)) + { + DEBUG(0,("Invalid snum %d for %s\n",iService,service)); + iService = -1; + } + + if (iService < 0) + DEBUG(3,("find_service() failed to find service %s\n", service)); + + return (iService); +} + + +/**************************************************************************** + make a connection to a service +****************************************************************************/ +connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode) +{ + int snum; + struct passwd *pass = NULL; + BOOL guest = False; + BOOL force = False; + extern int Client; + connection_struct *conn; + + strlower(service); + + snum = find_service(service); + if (snum < 0) { + extern int Client; + if (strequal(service,"IPC$")) { + DEBUG(3,("refusing IPC connection\n")); + *ecode = ERRnoipc; + return NULL; + } + + DEBUG(0,("%s (%s) couldn't find service %s\n", + remote_machine, client_addr(Client), service)); + *ecode = ERRinvnetname; + return NULL; + } + + if (strequal(service,HOMES_NAME)) { + if (*user && Get_Pwnam(user,True)) + return(make_connection(user,user,password, + pwlen,dev,vuid,ecode)); + + if(lp_security() != SEC_SHARE) { + if (validated_username(vuid)) { + pstrcpy(user,validated_username(vuid)); + return(make_connection(user,user,password,pwlen,dev,vuid,ecode)); + } + } else { + /* Security = share. Try with sesssetup_user + * as the username. */ + if(*sesssetup_user) { + pstrcpy(user,sesssetup_user); + return(make_connection(user,user,password,pwlen,dev,vuid,ecode)); + } + } + } + + if (!lp_snum_ok(snum) || + !check_access(Client, + lp_hostsallow(snum), lp_hostsdeny(snum))) { + *ecode = ERRaccess; + return NULL; + } + + /* you can only connect to the IPC$ service as an ipc device */ + if (strequal(service,"IPC$")) + pstrcpy(dev,"IPC"); + + if (*dev == '?' || !*dev) { + if (lp_print_ok(snum)) { + pstrcpy(dev,"LPT1:"); + } else { + pstrcpy(dev,"A:"); + } + } + + /* if the request is as a printer and you can't print then refuse */ + strupper(dev); + if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) { + DEBUG(1,("Attempt to connect to non-printer as a printer\n")); + *ecode = ERRinvdevice; + return NULL; + } + + /* lowercase the user name */ + strlower(user); + + /* add it as a possible user name */ + add_session_user(service); + + /* shall we let them in? */ + if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid)) { + DEBUG( 2, ( "Invalid username/password for %s\n", service ) ); + *ecode = ERRbadpw; + return NULL; + } + + conn = conn_new(); + if (!conn) { + DEBUG(0,("Couldn't find free connection.\n")); + *ecode = ERRnoresource; + conn_free(conn); + return NULL; + } + + /* find out some info about the user */ + pass = Get_Pwnam(user,True); + + if (pass == NULL) { + DEBUG(0,( "Couldn't find account %s\n",user)); + *ecode = ERRbaduid; + conn_free(conn); + return NULL; + } + + conn->read_only = lp_readonly(snum); + + { + pstring list; + StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1); + string_sub(list,"%S",service); + + if (user_in_list(user,list)) + conn->read_only = True; + + StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1); + string_sub(list,"%S",service); + + if (user_in_list(user,list)) + conn->read_only = False; + } + + /* admin user check */ + + /* JRA - original code denied admin user if the share was + marked read_only. Changed as I don't think this is needed, + but old code left in case there is a problem here. + */ + if (user_in_list(user,lp_admin_users(snum)) +#if 0 + && !conn->read_only +#endif + ) { + conn->admin_user = True; + DEBUG(0,("%s logged in as admin user (root privileges)\n",user)); + } else { + conn->admin_user = False; + } + + conn->force_user = force; + conn->vuid = vuid; + conn->uid = pass->pw_uid; + conn->gid = pass->pw_gid; + conn->num_files_open = 0; + conn->lastused = time(NULL); + conn->service = snum; + conn->used = True; + conn->printer = (strncmp(dev,"LPT",3) == 0); + conn->ipc = (strncmp(dev,"IPC",3) == 0); + conn->dirptr = NULL; + conn->veto_list = NULL; + conn->hide_list = NULL; + conn->veto_oplock_list = NULL; + string_set(&conn->dirpath,""); + string_set(&conn->user,user); + +#ifdef HAVE_GETGRNAM + if (*lp_force_group(snum)) { + struct group *gptr; + pstring gname; + + StrnCpy(gname,lp_force_group(snum),sizeof(pstring)-1); + /* default service may be a group name */ + string_sub(gname,"%S",service); + gptr = (struct group *)getgrnam(gname); + + if (gptr) { + conn->gid = gptr->gr_gid; + DEBUG(3,("Forced group %s\n",gname)); + } else { + DEBUG(1,("Couldn't find group %s\n",gname)); + } + } +#endif + + if (*lp_force_user(snum)) { + struct passwd *pass2; + fstring fuser; + fstrcpy(fuser,lp_force_user(snum)); + pass2 = (struct passwd *)Get_Pwnam(fuser,True); + if (pass2) { + conn->uid = pass2->pw_uid; + string_set(&conn->user,fuser); + fstrcpy(user,fuser); + conn->force_user = True; + DEBUG(3,("Forced user %s\n",fuser)); + } else { + DEBUG(1,("Couldn't find user %s\n",fuser)); + } + } + + { + pstring s; + pstrcpy(s,lp_pathname(snum)); + standard_sub(conn,s); + string_set(&conn->connectpath,s); + DEBUG(3,("Connect path is %s\n",s)); + } + + /* groups stuff added by ih */ + conn->ngroups = 0; + conn->groups = NULL; + + if (!IS_IPC(conn)) { + /* Find all the groups this uid is in and + store them. Used by become_user() */ + setup_groups(conn->user,conn->uid,conn->gid, + &conn->ngroups,&conn->groups); + + /* check number of connections */ + if (!claim_connection(conn, + lp_servicename(SNUM(conn)), + lp_max_connections(SNUM(conn)), + False)) { + DEBUG(1,("too many connections - rejected\n")); + *ecode = ERRnoresource; + conn_free(conn); + return NULL; + } + + if (lp_status(SNUM(conn))) + claim_connection(conn,"STATUS.", + MAXSTATUS,False); + } /* IS_IPC */ + + /* execute any "root preexec = " line */ + if (*lp_rootpreexec(SNUM(conn))) { + pstring cmd; + pstrcpy(cmd,lp_rootpreexec(SNUM(conn))); + standard_sub(conn,cmd); + DEBUG(5,("cmd=%s\n",cmd)); + smbrun(cmd,NULL,False); + } + + if (!become_user(conn, conn->vuid)) { + DEBUG(0,("Can't become connected user!\n")); + if (!IS_IPC(conn)) { + yield_connection(conn, + lp_servicename(SNUM(conn)), + lp_max_connections(SNUM(conn))); + if (lp_status(SNUM(conn))) { + yield_connection(conn,"STATUS.",MAXSTATUS); + } + } + conn_free(conn); + *ecode = ERRbadpw; + return NULL; + } + + if (ChDir(conn->connectpath) != 0) { + DEBUG(0,("Can't change directory to %s (%s)\n", + conn->connectpath,strerror(errno))); + unbecome_user(); + if (!IS_IPC(conn)) { + yield_connection(conn, + lp_servicename(SNUM(conn)), + lp_max_connections(SNUM(conn))); + if (lp_status(SNUM(conn))) + yield_connection(conn,"STATUS.",MAXSTATUS); + } + conn_free(conn); + *ecode = ERRinvnetname; + return NULL; + } + + string_set(&conn->origpath,conn->connectpath); + +#if SOFTLINK_OPTIMISATION + /* resolve any soft links early */ + { + pstring s; + pstrcpy(s,conn->connectpath); + GetWd(s); + string_set(&conn->connectpath,s); + ChDir(conn->connectpath); + } +#endif + + add_session_user(user); + + /* execute any "preexec = " line */ + if (*lp_preexec(SNUM(conn))) { + pstring cmd; + pstrcpy(cmd,lp_preexec(SNUM(conn))); + standard_sub(conn,cmd); + smbrun(cmd,NULL,False); + } + + /* we've finished with the sensitive stuff */ + unbecome_user(); + + /* Add veto/hide lists */ + if (!IS_IPC(conn) && !IS_PRINT(conn)) { + set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn))); + set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn))); + set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn))); + } + + if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { + extern int Client; + + dbgtext( "%s (%s) ", remote_machine, client_addr(Client) ); + dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) ); + dbgtext( "as user %s ", user ); + dbgtext( "(uid=%d, gid=%d) ", conn->uid, conn->gid ); + dbgtext( "(pid %d)\n", (int)getpid() ); + } + + return(conn); +} + + +/**************************************************************************** +close a cnum +****************************************************************************/ +void close_cnum(connection_struct *conn, uint16 vuid) +{ + extern int Client; + DirCacheFlush(SNUM(conn)); + + unbecome_user(); + + DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n", + remote_machine,client_addr(Client), + lp_servicename(SNUM(conn)))); + + yield_connection(conn, + lp_servicename(SNUM(conn)), + lp_max_connections(SNUM(conn))); + + if (lp_status(SNUM(conn))) + yield_connection(conn,"STATUS.",MAXSTATUS); + + file_close_conn(conn); + dptr_closecnum(conn); + + /* execute any "postexec = " line */ + if (*lp_postexec(SNUM(conn)) && + become_user(conn, vuid)) { + pstring cmd; + pstrcpy(cmd,lp_postexec(SNUM(conn))); + standard_sub(conn,cmd); + smbrun(cmd,NULL,False); + unbecome_user(); + } + + unbecome_user(); + /* execute any "root postexec = " line */ + if (*lp_rootpostexec(SNUM(conn))) { + pstring cmd; + pstrcpy(cmd,lp_rootpostexec(SNUM(conn))); + standard_sub(conn,cmd); + smbrun(cmd,NULL,False); + } + + conn_free(conn); +} + + |