#pragma implementation #include #include #include #include #include #include #include #include #include #include #include #include #include "fstab.h" #include "fixperm.h" #include #include #include #include #include "../paths.h" #include "fstab.m" #include #include static FSTAB_HELP_FILE help_perms ("perms"); /* #Specification: fixperm / strategy The directory /usr/lib/linuxconf/distribution/perm contains files describing the permissions of different packages. The format of the file is rather simple. # filespec owner group type permissions options ... # The permissions are expressed in octal. type is one of those: # b,major,minor: block device c,major,minor: character device f: file d: directory # Here are the options supported # boot: The check is done only at boot time. This is especially intended for pseudo tty which need special permissions. Those pty generally change while the system is running. Normally, everything is back to normal when the system goes down, except when it crash. Unless the permissions are set correctly, everything goes wrong: Debuggers, some editors etc... recurse: filespec must be a directory. The owner, group and permissions will be applied to all file in that directory and sudirectory. required: The file has to be there. An error will be signaled if not. If the file is a directory, it will be created. silent: Linuxconf will fix the permission on that file without asking permission. This is used for pseudo-tty. On those files, this is normal to fix permissions at boot time and this does not buy the user anything to know about it. # */ # const int maxopt=3; struct SPEC_ONE{ const char *fpath; // Path of the specification file int noline; // Current line in fpath char path[PATH_MAX]; char owner[PATH_MAX]; char group[PATH_MAX]; char type[PATH_MAX]; int perm; char opts[maxopt][PATH_MAX]; }; /* Print an error message with reference to the specification file */ static void fixperm_error (SPEC_ONE &sp, const char *msg, ...) { va_list list; va_start (list,msg); char buf[1000]; vsprintf (buf,msg,list); va_end (list); xconf_error (MSG_U(E_CONFFILE,"%s\n\n" "In file %s, line %d\n"),buf,sp.fpath,sp.noline); } /* Parse the option of one spec */ static int fixperm_parseopts (SPEC_ONE &sp, FIXPERM_OPTIONS &opts) { int ret = 0; opts.boot = opts.recurse = opts.required = opts.silent = false; for (int i=0; ipw_uid; } if (ge == NULL){ if (!silent){ xconf_error (MSG_U(E_NOGROUP ,"No group \"%s\" defined on this system\n" "Can't check permissions/ownership of the file or directory\n" "%s") ,group,path); } ret = -1; }else{ p.gid = ge->gr_gid; } return ret; } /* Fix one file specification */ PUBLIC int FIXPERM_SPEC::check() { PERMINFO p; if (valid){ /* This spec may refer to a file normally owned by a missing package. Further,the user/group of the spec are created by the package, so do not exist in the system yet. (For sure, fixperm specs should be supplied by the packages either using RPMS or something else. Unfortunatly, using would be too slow (checked everytime linuxconf exits). ) In the mean time, we call fixperm_readperm only if we we need to check the ownership and perm. This way, if the file/directory is missing, then the missing user/group are not important. */ struct stat64 st; const char *fpath = path.get(); int ok = stat64(fpath,&st); if ((ok != -1 || opts.required) && fixperm_readperm (path.get(),p,owner.get(),group.get(),perm ,false)!=-1){ int uid = p.uid; int gid = p.gid; perm = p.perm; owner.setfrom (p.owner); group.setfrom (p.group); bool dook = !simul_ison(); if (ok==-1){ /* #Specification: fixperm / symbolic links Symbolic links are often used to move things around in different file systems. The symbolic links are faking the original hierarchy. This complicates life for the "fixperm" functionnality of linuxconf. In fact, in different time, linuxconf can't produce a reliable status. For example my mail spool directory is on an NFS server. Instead of mounting the server spool in /var/spool/mail, I use the amd automounter and set a symbolic links like this. ln -s /n/server/var/spool/mail /var/spool/mail Unfortunatly, linuxconf can't test the validity of /var/spool/mail until the network is up. This checking is annoying. It complains that /var/spool/mail is not a directory and so on. So here is the patch. If a "something" has been replaced by a symbolic links and the destination of the link appears to be missing, linuxconf won't complain at all. */ if (opts.required && lstat64(fpath,&st)==-1){ net_prtlog (NETLOG_CMD,MSG_U(L_CREATING ,"Creating %s %s\n") ,S_ISDIR(perm) ? MSG_U(L_DIRECTORY,"directory") : MSG_U(L_FILE,"file") ,fpath); if (dook) create(p); } }else if ((st.st_mode & S_IFMT) != (mode_t)(perm & S_IFMT)){ net_prtlog (NETLOG_ERR ,MSG_U(E_CANTCHG ,"**** Can't change the type of file %s\n" " manual action required!\n") ,fpath); }else if ((S_ISBLK(perm) || S_ISCHR(perm)) && st.st_rdev != (dev_t)dev){ net_prtlog (NETLOG_ERR ,MSG_U(E_WRONGDEV,"Device file %s wrongly created\n" "\tExpected major %d, minor %d\n" "\tFound major %d, minor %d\n" "\tManual action required\n") ,fpath,dev>>8,dev&0xff,st.st_rdev>>8,st.st_rdev&0xff); }else{ char realpath[PATH_MAX]; file_followlink (fpath,realpath); if (st.st_uid != (uid_t) uid || st.st_gid != (gid_t)gid){ if (dook) chown (realpath,uid,gid); net_prtlog (NETLOG_CMD,MSG_U(L_CHGOWN,"Changing owner of file %s " "to %s.%s\n") ,realpath,owner.get(),group.get()); stat64(realpath,&st); } if (st.st_mode != (mode_t)perm){ if (dook) chmod (realpath,perm); net_prtlog (NETLOG_CMD ,MSG_U(L_CHGPERM ,"Changing permissions of file %s " "from %o to %o\n") ,realpath,st.st_mode,perm); } } } } return 0; } /* Fonction generally used by the FIXPERM_TASK to create a FIXPERM_SPECS on the fly */ PRIVATE void FIXPERM_SPECS::addline (const char *buf, SPEC_ONE &sp) { sp.opts[0][0] = sp.opts[1][0] = sp.opts[2][0] = '\0'; if (sscanf(buf,"%s %s %s %s %o %s %s %s\n" ,sp.path,sp.owner,sp.group,sp.type,&sp.perm ,sp.opts[0],sp.opts[1],sp.opts[2]) < 5){ fixperm_error (sp,MSG_U(E_IVLDLINE,"Invalid line: %s"),buf); }else{ add (new FIXPERM_SPEC (sp)); } } /* Fonction generally used by the FIXPERM_TASK to create a FIXPERM_SPECS on the fly */ PUBLIC void FIXPERM_SPECS::addline (const char *buf) { SPEC_ONE sp; sp.noline = -1; sp.fpath = "-internal-"; addline (buf,sp); } /* Read a set of fixperms from an ASCII file Return -1 if it can't read the file. */ PUBLIC int FIXPERM_SPECS::readspecf(const char *fname) { int ret = -1; SPEC_ONE sp; sp.fpath = fname; sp.noline = 1; FILE *fin = xconf_fopen (sp.fpath,"r"); if (fin != NULL){ char buf[500]; /* #Specification: fixperm / file format / comments The fixperm's spec file may contain blank lines and line beginning with a # are comments. */ while (fgets (buf,sizeof(buf)-1,fin)!=NULL){ strip_end (buf); char *ptbuf = str_skip(buf); if (ptbuf[0] != '\0' && ptbuf[0] != '#'){ addline (buf,sp); } sp.noline++; } fclose (fin); ret = 0; } return ret; } /* Check and optionnally ajust permissions on some files */ PUBLIC FIXPERM_SPECS::FIXPERM_SPECS(const char *fname) { readspecf (fname); } PUBLIC FIXPERM_SPECS::FIXPERM_SPECS() { } PUBLIC FIXPERM_SPEC *FIXPERM_SPECS::getitem(int no) { return (FIXPERM_SPEC*)ARRAY::getitem(no); } PUBLIC int FIXPERM_SPECS::check(bool boottime, bool silentflag) { int ret = 0; for (int i=0; iopts.silent == silentflag){ if (chk->opts.boot){ if (boottime){ if (chk->check() == -1) ret = -1; } }else{ if (chk->check() == -1) ret = -1; } } } return ret; } PUBLIC int FIXPERM_SPECS::check() { return check (false,false); } /* Fix the path of a perm file to select the one more appropriate to the distribution version. */ static void fixperm_fixpath ( const char *prefix, // General prefix const char *file, char path[PATH_MAX]) { char prefix_brev[PATH_MAX]; // Prefix for the base version of the distribution char prefix_rev[PATH_MAX]; // For the distribution version // prefix is something like /usr/lib/linuxconf/redhat/perm // prefix_brev is prefix + /6 for any redhat 6.x // prefix_rev is prefix + / sprintf (prefix_rev,"%s/%s",prefix,distrib_getrelease()); strcpy (prefix_brev,prefix_rev); { char *pt = strchr(prefix_brev,'.'); if (pt != NULL) *pt = '\0'; } if (file[0] != '\0'){ sprintf (path,"%s/%s",prefix_rev,file); if (!file_exist(path)){ sprintf (path,"%s/%s",prefix_brev,file); if (!file_exist(path)){ sprintf (path,"%s/%s",prefix,file); } } } } static int fixperm_check ( const char *prefix, // General prefix SSTRINGS &lst, bool boottime, bool silentflag) { int ret = 0; for (int i=0; iget(); if (ptf[0] != '\0'){ char path[PATH_MAX]; fixperm_fixpath (prefix,ptf,path); FIXPERM_SPECS specs(path); if (specs.check(boottime,silentflag) == -1) ret = -1; } } return ret; } static void fixperm_getspecf( SSTRINGS &usr_tb, // Will contains perm files in /usr/lib/linuxconf/... SSTRINGS &var_tb, // Will contains perm files in /var/lib/... char pathperm[PATH_MAX]) { /* #Specification: fixperm / /var/lib/conf.permissions / strategy Linuxconf provides its own set of permissions files int /usr/lib/linuxconf/DIST/perm/. These are supplied with linuxconf and are updated at each new releases. You can create permission files in /var/lib/conf.permissions. If you create a permission file with the same name as one in /usr/lib/linuxconf/DIST/perm/, yours will take precedence. This allows you to do customisation of those file without fearing the next update :-) */ linuxconf_fixdistdir (USR_LIB_CONF_PERMISSIONS,pathperm); dir_getlist(pathperm,usr_tb); dir_getlist(VAR_LIB_CONF_PERMISSIONS,var_tb); for (int v=0; vsetfrom(""); } } /* Check and optionnally ajust permissions on some files Return -1 if any error. */ static int fixperm_check(bool boottime, bool silentflag) { SSTRINGS usr_tb, var_tb; char pathperm[PATH_MAX]; fixperm_getspecf(usr_tb,var_tb,pathperm); return fixperm_check (pathperm,usr_tb,boottime,silentflag) | fixperm_check (VAR_LIB_CONF_PERMISSIONS,var_tb,boottime,silentflag); } /* #Spécification: FIXPERM_TASK / principle Each sub-system needing a special handling for file permission checking may define its own FIXPERM_TASK object and override the check() function. It will be called whenever linuxconf is doing some checks */ static FIXPERM_TASK *first_task=NULL; PUBLIC FIXPERM_TASK::FIXPERM_TASK() { next = first_task; first_task = this; } PUBLIC VIRTUAL FIXPERM_TASK::~FIXPERM_TASK() { // We unlink from the link list FIXPERM_TASK **ptpt = &first_task; while (*ptpt != NULL){ if (*ptpt == this){ *ptpt = next; break; } ptpt = &(*ptpt)->next; } } static int fixperm_tasks (bool boottime, bool silentflag) { int ret = 0; FIXPERM_TASK *pt = first_task; while (pt != NULL){ ret |= pt->check(boottime,silentflag); pt = pt->next; } return ret; } static int fixperm_check_all(bool bootflag, bool silentflag) { int ret = fixperm_check(bootflag,silentflag); ret |= module_fixperm(bootflag,silentflag); ret |= fixperm_tasks(bootflag,silentflag); return ret; } /* Check and optionnally ajust permissions on some files Return -1 if any error. */ int fixperm_check() { return fixperm_check_all (false,false); } /* Check and optionnally ajust permissions on some files at boot time Return -1 if any error. */ int fixperm_check_boot() { fixperm_check_all(true,true); simul_init(); int ret = fixperm_check_all(true,false); if (ret != -1 && simul_prompt(false)==1){ net_setshowmode (0); ret = fixperm_check_all(true,false); net_setshowmode (1); } return ret; } /* Check one system (used by linuxconf's modules) */ int fixperm_checkone ( const char *path, bool boottime, bool silentflag) { FIXPERM_SPECS specs(path); return specs.check(boottime,silentflag); } /* Check and optionnally ajust permissions on some system */ static int fixperm_check(int nb, char *tb[]) { int ret; /* #Specification: fixperm / command line / args "fixperm --update" or "fixperm --status" without further arguments will check all system in /usr/lib/linuxconf/conf.permissions/ and /var/lib/conf.permissions/. If some argument are provided, they are taken as specific system to check in either /usr/lib/linuxconf/conf.permissions/ or /var/lib/conf.permissions/. If one argument start with a /, it is taken as an absolute path to a specification file using the same format as the ones in /usr/lib/linuxconf/conf.permissions/. */ if (nb == 0){ ret = fixperm_check(); // Check all }else{ ret = 0; SSTRINGS usr_tb; char usrperm[PATH_MAX]; linuxconf_fixdistdir (USR_LIB_CONF_PERMISSIONS,usrperm); dir_getlist(usrperm,usr_tb); SSTRINGS var_tb; dir_getlist(VAR_LIB_CONF_PERMISSIONS,var_tb); for (int i=0; iget(); if (ptf[0] != '\0'){ char path[PATH_MAX]; fixperm_fixpath (prefix,ptf,path); specs.readspecf (path); } } } /* Return -1 if edit was aborted */ PUBLIC int FIXPERM_SPEC::edit() { PERMINFO p; fixperm_readperm (path.get(),p,owner.get(),group.get(),perm,false); DIALOG dia; dia.newf_str (MSG_U(F_PATH,"Path"),path); dia.set_lastreadonly(); dia.newf_str (MSG_U(F_OWNER,"Owner"),p.owner); dia.newf_str (MSG_U(F_GROUP,"Group"),p.group); char permflg[12]; int bit = 0400; int b; for (b=0; b<9; b++, bit>>=1){ static const char *tbflag[]={ MSG_U(F_MAYREAD,"May read"), MSG_U(F_MAYWRITE,"May write"), MSG_U(F_MAYEXEC,"May execute"), }; permflg[b+3] = ((p.perm & bit) == 0) ? 0 : 1; const char *title = ""; if (b % 3 == 0){ static const char *tbt[]= { MSG_U(T_USER,"User"), MSG_U(T_GROUP,"Group"), MSG_U(T_OTHER,"Others") }; title = tbt[b/3]; } dia.newf_chk (title,permflg[b+3],tbflag[b % 3]); } dia.newf_title ("",MSG_U(T_SPCBITS,"Special flags")); bit = 04000; for (b=0; b<3; b++, bit>>=1){ static const char *tbflag[]={ MSG_U(F_SETUID,"Setuid flag"), MSG_U(F_SETGID,"Setgid flag"), MSG_U(F_ONLYOWNER,"Restriction on directory"), }; permflg[b] = ((p.perm & bit) == 0) ? 0 : 1; dia.newf_chk ("",permflg[b],tbflag[b % 3]); } int nof = 0; int ret = -1; while (1){ MENU_STATUS code = dia.edit_form ( MSG_U(T_ONESPEC,"One file permission setting") ,"" ,help_perms ,nof,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_RESET); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_RESET){ p.owner.setfrom (owner); p.group.setfrom (group); p.perm = perm; int bit = 04000; for (b=0; b<12; b++, bit>>=1){ permflg[b] = ((p.perm & bit) == 0) ? 0 : 1; } //dia.reload(); xconf_notice ("Not done yet"); }else if (code == MENU_ACCEPT){ int bit = 04000; for (b=0; b<12; b++, bit>>=1){ if (permflg[b]){ p.perm |= bit; }else{ p.perm &= (~bit); } } const char *powner = p.owner.get(); const char *pgroup = p.group.get(); if (getpwnam (powner)==NULL){ xconf_error (MSG_R(E_NOUSER),powner); nof = 1; }else if (getgrnam (pgroup)==NULL){ xconf_error (MSG_R(E_NOGROUP),pgroup); nof = 1; }else{ if (owner.cmp(powner)!=0 || group.cmp(pgroup)!=0 || perm != p.perm){ char buf[200]; sprintf (buf,"%o %s %s",p.perm,powner,pgroup); linuxconf_replace (PERMS,path.get(),buf); }else{ linuxconf_removeall (PERMS,path.get()); } linuxconf_save (); ret = 0; break; } } } return ret; } /* Present all files and directory managed by linuxconf and allows tweaking of permissions */ void fstab_editperm() { glocal FIXPERM_SPECS specs; { SSTRINGS usr_tb, var_tb; char pathperm[PATH_MAX]; fixperm_getspecf(usr_tb,var_tb,pathperm); fstab_loadspecs (glocal.specs,pathperm,usr_tb); fstab_loadspecs (glocal.specs,VAR_LIB_CONF_PERMISSIONS,var_tb); } { FIXPERM_TASK *pt = first_task; while (pt != NULL){ pt->list(glocal.specs); pt = pt->next; } } //specs.sort(cmp_by_path); (MSG_U(T_PERMS,"Permissions of all config files") ,MSG_U(I_PERMS,"You can see and change the permission\n" "settings of all configuration files and directories\n" "managed by linuxconf.") ,help_perms); newf_head (MSG_U(H_PERMS,"Path\tType\tOwner\tPermissions\tMod")); sortable(); sortpolicy("aaaaa"); int n = glocal.specs.getnb(); for (int i=0; iperm; int bit = 0400; for (int b=0; b<9; b++, bit>>=1){ if ((perm & bit) == 0) permstr[b] = ' '; } if (perm & 04000) permstr[2] = 's'; if (perm & 02000) permstr[5] = 's'; if (perm & 01000) permstr[8] = 't'; const char *type = "?"; if (S_ISDIR(perm)){ type = MSG_U(T_DIR,"Dir"); }else if (S_ISREG(perm)){ type = MSG_U(T_FILE,"File"); }else if (S_ISCHR(perm)){ type = MSG_U(T_CHR,"Chrdev"); }else if (S_ISBLK(perm)){ type = MSG_U(T_BLK,"Blkdev"); } char mod = ' '; const char *path = s->path.get(); if (linuxconf_getval(PERMS,path)!=NULL) mod = '*'; new_menuitemf (path,"%s\t%s %s\t%s\t%c",type ,s->owner.get(),s->group.get() ,permstr,mod); } FIXPERM_SPEC *s = glocal.specs.getitem(no); s->edit(); } static const char K_FIXPERM[]="fixperm"; static const char K_ENABLE[]="enable"; /* call fixperm_check() if configured to do automatic permission check */ void fixperm_probe() { if (linuxconf_getvalnum(K_FIXPERM,K_ENABLE,1)!=0){ fixperm_check(); } } struct FIXPERM_DIA: public ARRAY_OBJ{ bool check; FIXPERM_DIA(){ check = linuxconf_getvalnum(K_FIXPERM,K_ENABLE,1); }; }; ("features"); FIXPERM_DIA *mydata = new FIXPERM_DIA; dia.newf_title (MSG_U(T_FSTABMOD,"Module fsconf"),1,"",MSG_R(T_FSTABMOD)); dia.newf_chk ("",mydata->check,MSG_U(I_FIXPERMENABLE,"Enable automatic fixperm")); dia.addhelp (help_perms,MSG_R(T_FSTABMOD)); data = mydata; FIXPERM_DIA *mydata = (FIXPERM_DIA*)data; linuxconf_replace (K_FIXPERM,K_ENABLE,mydata->check); linuxconf_save (privi); return 0;