The big character set handling changeover!
[ira/wip.git] / source3 / smbd / service.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    service (connection) opening and closing
5    Copyright (C) Andrew Tridgell 1992-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 extern int DEBUGLEVEL;
25
26 extern struct timeval smb_last_time;
27 extern int case_default;
28 extern BOOL case_preserve;
29 extern BOOL short_case_preserve;
30 extern BOOL case_mangle;
31 extern BOOL case_sensitive;
32 extern BOOL use_mangled_map;
33 extern fstring remote_machine;
34 extern userdom_struct current_user_info;
35 extern fstring remote_machine;
36
37
38 /****************************************************************************
39 load parameters specific to a connection/service
40 ****************************************************************************/
41 BOOL become_service(connection_struct *conn,BOOL do_chdir)
42 {
43         extern char magic_char;
44         static connection_struct *last_conn;
45         int snum;
46
47         if (!conn)  {
48                 last_conn = NULL;
49                 return(False);
50         }
51
52         conn->lastused = smb_last_time.tv_sec;
53
54         snum = SNUM(conn);
55   
56         if (do_chdir &&
57             vfs_ChDir(conn,conn->connectpath) != 0 &&
58             vfs_ChDir(conn,conn->origpath) != 0) {
59                 DEBUG(0,("chdir (%s) failed\n",
60                          conn->connectpath));
61                 return(False);
62         }
63
64         if (conn == last_conn)
65                 return(True);
66
67         last_conn = conn;
68
69         case_default = lp_defaultcase(snum);
70         case_preserve = lp_preservecase(snum);
71         short_case_preserve = lp_shortpreservecase(snum);
72         case_mangle = lp_casemangle(snum);
73         case_sensitive = lp_casesensitive(snum);
74         magic_char = lp_magicchar(snum);
75         use_mangled_map = (*lp_mangled_map(snum) ? True:False);
76         return(True);
77 }
78
79 /****************************************************************************
80  Add a home service. Returns the new service number or -1 if fail.
81 ****************************************************************************/
82
83 int add_home_service(char *service, char *homedir)
84 {
85         int iHomeService;
86         int iService;
87         fstring new_service;
88         char *usr_p = NULL;
89
90         if (!service || !homedir)
91                 return -1;
92
93         if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
94                 return -1;
95
96         /*
97          * If this is a winbindd provided username, remove
98          * the domain component before adding the service.
99          * Log a warning if the "path=" parameter does not
100          * include any macros.
101          */
102
103         fstrcpy(new_service, service);
104
105         if ((usr_p = strchr(service,*lp_winbind_separator())) != NULL)
106                 fstrcpy(new_service, usr_p+1);
107
108         lp_add_home(new_service,iHomeService,homedir);
109         iService = lp_servicenumber(new_service);
110
111         return iService;
112 }
113
114 /****************************************************************************
115  Find a service entry. service is always in dos codepage.
116 ****************************************************************************/
117
118 int find_service(char *service)
119 {
120    int iService;
121
122    all_string_sub(service,"\\","/",0);
123
124    iService = lp_servicenumber(service);
125
126    /* now handle the special case of a home directory */
127    if (iService < 0)
128    {
129       char *phome_dir = get_user_home_dir(service);
130
131       if(!phome_dir)
132       {
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,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    {
150       int iPrinterService;
151
152       if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0)
153       {
154          char *pszTemp;
155
156          DEBUG(3,("checking whether %s is a valid printer name...\n", service));
157          pszTemp = PRINTCAP;
158          if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp))
159          {
160             DEBUG(3,("%s is a valid printer name\n", service));
161             DEBUG(3,("adding %s as a printer service\n", service));
162             lp_add_printer(service,iPrinterService);
163             iService = lp_servicenumber(service);
164             if (iService < 0)
165                DEBUG(0,("failed to add %s as a printer service!\n", service));
166          }
167          else
168             DEBUG(3,("%s is not a valid printer name\n", service));
169       }
170    }
171
172    /* Check for default vfs service?  Unsure whether to implement this */
173    if (iService < 0)
174    {
175    }
176
177    /* just possibly it's a default service? */
178    if (iService < 0) 
179    {
180      char *pdefservice = lp_defaultservice();
181      if (pdefservice && *pdefservice && 
182          !strequal(pdefservice,service) &&
183          !strstr(service,".."))
184      {
185        /*
186         * We need to do a local copy here as lp_defaultservice() 
187         * returns one of the rotating lp_string buffers that
188         * could get overwritten by the recursive find_service() call
189         * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
190         */
191        pstring defservice;
192        pstrcpy(defservice, pdefservice);
193        iService = find_service(defservice);
194        if (iService >= 0)
195        {
196          all_string_sub(service,"_","/",0);
197          iService = lp_add_service(service,iService);
198        }
199      }
200    }
201
202    if (iService >= 0)
203      if (!VALID_SNUM(iService))
204      {
205        DEBUG(0,("Invalid snum %d for %s\n",iService,service));
206        iService = -1;
207      }
208
209    if (iService < 0)
210      DEBUG(3,("find_service() failed to find service %s\n", service));
211
212    return (iService);
213 }
214
215
216 /****************************************************************************
217   make a connection to a service
218 ****************************************************************************/
219 connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode)
220 {
221         int snum;
222         struct passwd *pass = NULL;
223         BOOL guest = False;
224         BOOL force = False;
225         connection_struct *conn;
226         int ret;
227
228         strlower(service);
229
230         snum = find_service(service);
231         if (snum < 0) {
232                 if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) {
233                         DEBUG(3,("refusing IPC connection\n"));
234                         *ecode = ERRnoipc;
235                         return NULL;
236                 }
237
238                 DEBUG(0,("%s (%s) couldn't find service %s\n",
239                          remote_machine, client_addr(), service));
240                 *ecode = ERRnosuchshare;
241                 return NULL;
242         }
243
244         if (strequal(service,HOMES_NAME)) {
245                 if (*user && Get_Pwnam(user,True)) {
246                         fstring dos_username;
247                         fstrcpy(dos_username, user);
248                         return(make_connection(dos_username,user,password,
249                                                pwlen,dev,vuid,ecode));
250                 }
251
252                 if(lp_security() != SEC_SHARE) {
253                         if (validated_username(vuid)) {
254                                 fstring dos_username;
255                                 fstrcpy(user,validated_username(vuid));
256                                 fstrcpy(dos_username, user);
257                                 return(make_connection(dos_username,user,password,pwlen,dev,vuid,ecode));
258                         }
259                 } else {
260                         /* Security = share. Try with current_user_info.smb_name
261                          * as the username.  */
262                         if(*current_user_info.smb_name) {
263                                 fstring dos_username;
264                                 fstrcpy(user,current_user_info.smb_name);
265                                 fstrcpy(dos_username, user);
266                                 return(make_connection(dos_username,user,password,pwlen,dev,vuid,ecode));
267                         }
268                 }
269         }
270
271         if (!lp_snum_ok(snum) || 
272             !check_access(smbd_server_fd(), 
273                           lp_hostsallow(snum), lp_hostsdeny(snum))) {    
274                 *ecode = ERRaccess;
275                 return NULL;
276         }
277
278         /* you can only connect to the IPC$ service as an ipc device */
279         if (strequal(service,"IPC$") || strequal(service,"ADMIN$"))
280                 pstrcpy(dev,"IPC");
281         
282         if (*dev == '?' || !*dev) {
283                 if (lp_print_ok(snum)) {
284                         pstrcpy(dev,"LPT1:");
285                 } else {
286                         pstrcpy(dev,"A:");
287                 }
288         }
289
290         /* if the request is as a printer and you can't print then refuse */
291         strupper(dev);
292         if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) {
293                 DEBUG(1,("Attempt to connect to non-printer as a printer\n"));
294                 *ecode = ERRinvdevice;
295                 return NULL;
296         }
297
298         /* Behave as a printer if we are supposed to */
299         if (lp_print_ok(snum) && (strcmp(dev, "A:") == 0)) {
300                 pstrcpy(dev, "LPT1:");
301         }
302
303         /* lowercase the user name */
304         strlower(user);
305
306         /* add it as a possible user name if we 
307            are in share mode security */
308         if (lp_security() == SEC_SHARE) {
309                 add_session_user(service);
310         }
311
312         /* shall we let them in? */
313         if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid)) {
314                 DEBUG( 2, ( "Invalid username/password for %s [%s]\n", service, user ) );
315                 *ecode = ERRbadpw;
316                 return NULL;
317         }
318   
319         conn = conn_new();
320         if (!conn) {
321                 DEBUG(0,("Couldn't find free connection.\n"));
322                 *ecode = ERRnoresource;
323                 return NULL;
324         }
325
326         /* find out some info about the user */
327         pass = smb_getpwnam(user,True);
328
329         if (pass == NULL) {
330                 DEBUG(0,( "Couldn't find account %s\n",user));
331                 *ecode = ERRbaduid;
332                 conn_free(conn);
333                 return NULL;
334         }
335
336         conn->read_only = lp_readonly(snum);
337
338
339         {
340                 pstring list;
341                 StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1);
342                 pstring_sub(list,"%S",service);
343
344                 if (user_in_list(user,list))
345                         conn->read_only = True;
346                 
347                 StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1);
348                 pstring_sub(list,"%S",service);
349                 
350                 if (user_in_list(user,list))
351                         conn->read_only = False;    
352         }
353
354         /* admin user check */
355         
356         /* JRA - original code denied admin user if the share was
357            marked read_only. Changed as I don't think this is needed,
358            but old code left in case there is a problem here.
359         */
360         if (user_in_list(user,lp_admin_users(snum)) 
361 #if 0
362             && !conn->read_only
363 #endif
364             ) {
365                 conn->admin_user = True;
366                 DEBUG(0,("%s logged in as admin user (root privileges)\n",user));
367         } else {
368                 conn->admin_user = False;
369         }
370     
371         conn->force_user = force;
372         conn->vuid = vuid;
373         conn->uid = pass->pw_uid;
374         conn->gid = pass->pw_gid;
375         safe_strcpy(conn->client_address, client_addr(), sizeof(conn->client_address)-1);
376         conn->num_files_open = 0;
377         conn->lastused = time(NULL);
378         conn->service = snum;
379         conn->used = True;
380         conn->printer = (strncmp(dev,"LPT",3) == 0);
381         conn->ipc = ((strncmp(dev,"IPC",3) == 0) || strequal(dev,"ADMIN$"));
382         conn->dirptr = NULL;
383         conn->veto_list = NULL;
384         conn->hide_list = NULL;
385         conn->veto_oplock_list = NULL;
386         string_set(&conn->dirpath,"");
387         string_set(&conn->user,user);
388         conn->nt_user_token = NULL;
389         
390         /*
391          * If force user is true, then store the
392          * given userid and also the primary groupid
393          * of the user we're forcing.
394          */
395         
396         if (*lp_force_user(snum)) {
397                 struct passwd *pass2;
398                 pstring fuser;
399                 pstrcpy(fuser,lp_force_user(snum));
400
401                 /* Allow %S to be used by force user. */
402                 pstring_sub(fuser,"%S",service);
403
404                 pass2 = (struct passwd *)Get_Pwnam(fuser,True);
405                 if (pass2) {
406                         conn->uid = pass2->pw_uid;
407                         conn->gid = pass2->pw_gid;
408                         string_set(&conn->user,fuser);
409                         fstrcpy(user,fuser);
410                         conn->force_user = True;
411                         DEBUG(3,("Forced user %s\n",fuser));      
412                 } else {
413                         DEBUG(1,("Couldn't find user %s\n",fuser));
414                 }
415         }
416
417         /* admin users always run as uid=0 */
418         if (conn->admin_user) {
419                 conn->uid = 0;
420         }
421
422 #ifdef HAVE_GETGRNAM 
423         /*
424          * If force group is true, then override
425          * any groupid stored for the connecting user.
426          */
427         
428         if (*lp_force_group(snum)) {
429                 gid_t gid;
430                 pstring gname;
431                 pstring tmp_gname;
432                 BOOL user_must_be_member = False;
433                 
434                 StrnCpy(tmp_gname,lp_force_group(snum),sizeof(pstring)-1);
435
436                 if (tmp_gname[0] == '+') {
437                         user_must_be_member = True;
438                         StrnCpy(gname,&tmp_gname[1],sizeof(pstring)-2);
439                 } else {
440                         StrnCpy(gname,tmp_gname,sizeof(pstring)-1);
441                 }
442                 /* default service may be a group name          */
443                 pstring_sub(gname,"%S",service);
444                 gid = nametogid(gname);
445                 
446                 if (gid != (gid_t)-1) {
447                         /*
448                          * If the user has been forced and the forced group starts
449                          * with a '+', then we only set the group to be the forced
450                          * group if the forced user is a member of that group.
451                          * Otherwise, the meaning of the '+' would be ignored.
452                          */
453                         if (conn->force_user && user_must_be_member) {
454                                 if (user_in_group_list( user, gname )) {
455                                                 conn->gid = gid;
456                                                 DEBUG(3,("Forced group %s for member %s\n",gname,user));
457                                 }
458                         } else {
459                                 conn->gid = gid;
460                                 DEBUG(3,("Forced group %s\n",gname));
461                         }
462                 } else {
463                         DEBUG(1,("Couldn't find group %s\n",gname));
464                 }
465         }
466 #endif /* HAVE_GETGRNAM */
467
468         {
469                 pstring s;
470                 pstrcpy(s,lp_pathname(snum));
471                 standard_sub_conn(conn,s);
472                 string_set(&conn->connectpath,s);
473                 DEBUG(3,("Connect path is %s\n",s));
474         }
475
476         /* groups stuff added by ih */
477         conn->ngroups = 0;
478         conn->groups = NULL;
479         
480         /* Find all the groups this uid is in and
481            store them. Used by become_user() */
482         initialise_groups(conn->user, conn->uid, conn->gid); 
483         get_current_groups(&conn->ngroups,&conn->groups);
484                 
485         /* check number of connections */
486         if (!claim_connection(conn,
487                               lp_servicename(SNUM(conn)),
488                               lp_max_connections(SNUM(conn)),
489                               False)) {
490                 DEBUG(1,("too many connections - rejected\n"));
491                 *ecode = ERRnoresource;
492                 conn_free(conn);
493                 return NULL;
494         }  
495                 
496         conn->nt_user_token = create_nt_token(conn->uid, conn->gid, 
497                                               conn->ngroups, conn->groups,
498                                               guest);
499
500         /*
501          * New code to check if there's a share security descripter
502          * added from NT server manager. This is done after the
503          * smb.conf checks are done as we need a uid and token. JRA.
504          */
505
506         {
507                 BOOL can_write = share_access_check(conn, snum, vuid, FILE_WRITE_DATA);
508
509                 if (!can_write) {
510                         if (!share_access_check(conn, snum, vuid, FILE_READ_DATA)) {
511                                 /* No access, read or write. */
512                                 *ecode = ERRaccess;
513                                 DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n",
514                                         service ));
515                                 conn_free(conn);
516                                 return NULL;
517                         } else {
518                                 conn->read_only = True;
519                         }
520                 }
521         }
522         /* Initialise VFS function pointers */
523
524         if (!vfs_init(conn)) {
525                 DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn))));
526                 conn_free(conn);
527                 return NULL;
528         }
529
530         /* execute any "root preexec = " line */
531         if (*lp_rootpreexec(SNUM(conn))) {
532                 pstring cmd;
533                 pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
534                 standard_sub_conn(conn,cmd);
535                 DEBUG(5,("cmd=%s\n",cmd));
536                 ret = smbrun(cmd,NULL);
537                 if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) {
538                         DEBUG(1,("preexec gave %d - failing connection\n", ret));
539                         conn_free(conn);
540                         *ecode = ERRsrverror;
541                         return NULL;
542                 }
543         }
544         
545         if (!become_user(conn, conn->vuid)) {
546                 DEBUG(0,("Can't become connected user!\n"));
547                 yield_connection(conn,
548                                  lp_servicename(SNUM(conn)),
549                                  lp_max_connections(SNUM(conn)));
550                 conn_free(conn);
551                 *ecode = ERRbadpw;
552                 return NULL;
553         }
554         
555         if (vfs_ChDir(conn,conn->connectpath) != 0) {
556                 DEBUG(0,("%s (%s) Can't change directory to %s (%s)\n",
557                          remote_machine, conn->client_address,
558                          conn->connectpath,strerror(errno)));
559                 unbecome_user();
560                 yield_connection(conn,
561                                  lp_servicename(SNUM(conn)),
562                                  lp_max_connections(SNUM(conn)));
563                 conn_free(conn);
564                 *ecode = ERRnosuchshare;
565                 return NULL;
566         }
567         
568         string_set(&conn->origpath,conn->connectpath);
569         
570 #if SOFTLINK_OPTIMISATION
571         /* resolve any soft links early */
572         {
573                 pstring s;
574                 pstrcpy(s,conn->connectpath);
575                 vfs_GetWd(conn,s);
576                 string_set(&conn->connectpath,s);
577                 vfs_ChDir(conn,conn->connectpath);
578         }
579 #endif
580         
581         add_session_user(user);
582                 
583         /* execute any "preexec = " line */
584         if (*lp_preexec(SNUM(conn))) {
585                 pstring cmd;
586                 pstrcpy(cmd,lp_preexec(SNUM(conn)));
587                 standard_sub_conn(conn,cmd);
588                 ret = smbrun(cmd,NULL);
589                 if (ret != 0 && lp_preexec_close(SNUM(conn))) {
590                         DEBUG(1,("preexec gave %d - failing connection\n", ret));
591                         conn_free(conn);
592                         *ecode = ERRsrverror;
593                         return NULL;
594                 }
595         }
596
597         /*
598          * Print out the 'connected as' stuff here as we need
599          * to know the effective uid and gid we will be using.
600          */
601
602         if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
603                 dbgtext( "%s (%s) ", remote_machine, conn->client_address );
604                 dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
605                 dbgtext( "as user %s ", user );
606                 dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() );
607                 dbgtext( "(pid %d)\n", (int)sys_getpid() );
608         }
609         
610         /* we've finished with the sensitive stuff */
611         unbecome_user();
612         
613         /* Add veto/hide lists */
614         if (!IS_IPC(conn) && !IS_PRINT(conn)) {
615                 set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn)));
616                 set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn)));
617                 set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn)));
618         }
619         
620         /* Invoke VFS make connection hook */
621
622         if (conn->vfs_ops.connect) {
623                 if (conn->vfs_ops.connect(conn, service, user) < 0)
624                         return NULL;
625         }
626             
627         return(conn);
628 }
629
630
631 /****************************************************************************
632 close a cnum
633 ****************************************************************************/
634 void close_cnum(connection_struct *conn, uint16 vuid)
635 {
636         DirCacheFlush(SNUM(conn));
637
638         unbecome_user();
639
640         DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
641                                  remote_machine,conn->client_address,
642                                  lp_servicename(SNUM(conn))));
643
644         if (conn->vfs_ops.disconnect != NULL) {
645
646             /* Call VFS disconnect hook */
647             
648             conn->vfs_ops.disconnect(conn);
649             
650         }
651
652         yield_connection(conn,
653                          lp_servicename(SNUM(conn)),
654                          lp_max_connections(SNUM(conn)));
655
656         file_close_conn(conn);
657         dptr_closecnum(conn);
658
659         /* execute any "postexec = " line */
660         if (*lp_postexec(SNUM(conn)) && 
661             become_user(conn, vuid))  {
662                 pstring cmd;
663                 pstrcpy(cmd,lp_postexec(SNUM(conn)));
664                 standard_sub_conn(conn,cmd);
665                 smbrun(cmd,NULL);
666                 unbecome_user();
667         }
668
669         unbecome_user();
670         /* execute any "root postexec = " line */
671         if (*lp_rootpostexec(SNUM(conn)))  {
672                 pstring cmd;
673                 pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
674                 standard_sub_conn(conn,cmd);
675                 smbrun(cmd,NULL);
676         }
677         conn_free(conn);
678 }
679
680