#include #include #include #include #include #include #include #include "userconf.h" #include "userconf.m" #include "usercomng.h" #include "internal.h" #include /* #Specification: privileges / intro The privilege system of Linuxconf let you give special power to end user. Linuxconf manage quite a few feature of a system and each component of linuxconf can associate itself with a special privilege. When trying to perform something special, the component ask permission to the perm_access() function passing it the PRIVILEGE object which fit its security scheme. When perm_access() is called, either the root password or the user password must be supplied. If the user has this privilege, then operation can continue. The privilege feature is unique to linuxconf. A privileged user has no special UID or GID. Out of linuxconf, it is a plain normal users. This concept is expect to grow in the following directions # -Assigning privilege to group. -Allowing some flexibility to user authentication. For exemple one user won't have to provide his password if he is accessing linuxconf in such or such context. This would help make linux much more user friendly. -Assigning password to privilege # PRIVILEGE objects are always static and link together, so we know at run time all the privilege that exist in the application. This is used to dunamically create the USER configuration dialog. */ static PRIVILEGE *first; PUBLIC PRIVILEGE::PRIVILEGE( const char *_id, // Key to store the privilege TRANS_NOTLOAD *_title, // Title shown in the user account dialog TRANS_NOTLOAD *_section)// Title of the section. All PRIVILEGE with // the same section title will be shown // together and the section title will be used // as a section separator in the dialog { msg.title = _title; msg.section = _section; id.setfrom (_id); next = first; first = this; } PUBLIC PRIVILEGE::PRIVILEGE( const char *_id, // Key to store the privilege const char *_title, // Title shown in the user account dialog const char *_section) // Title of the section. All PRIVILEGE with // the same section title will be shown // together and the section title will be used // as a section separator in the dialog { id.setfrom (_id); str.title.setfrom(_title); str.section.setfrom(_section); msg.title = NULL; msg.section = NULL; next = first; first = this; } PUBLIC PRIVILEGE::~PRIVILEGE() { PRIVILEGE **ptpt = &first; while (*ptpt != NULL){ if (*ptpt == this){ *ptpt = next; break; } ptpt = &(*ptpt)->next; } } /* #Spécification: PRIVILEGE_TASK / principle Each sub-system needing to create privilege dynamicly (not simply using "static PRIVILEGE" variable may define a PRIVILEGE_DECLARATOR object. The only purpose of this object is to pass a pointer to one function which will be called when privilege editing is taking place to make sure the privileges of this subsystem are created and up to date. */ static PRIVILEGE_DECLARATOR *first_declarator=NULL; PUBLIC PRIVILEGE_DECLARATOR::PRIVILEGE_DECLARATOR(void (*_fct)()) { fct = _fct; next = first_declarator; first_declarator = this; } /* Return the title of the privilege */ PUBLIC const char *PRIVILEGE::gettitle() { return msg.title != NULL ? msg.title->get() : str.title.get(); } /* Return the section title of the privilege */ PUBLIC const char *PRIVILEGE::getsection() { return msg.section != NULL ? msg.section->get() : str.section.get(); } /* Return != 0 if the user must identify itself (provide a password) */ PUBLIC int PRIVILEGE_DATA::mustident() { return active < 2; } /* Return != 0 if this user has the privilege */ PUBLIC int PRIVILEGE_DATA::has_priv() { return active != 0; } class PRIVILEGE_DATA_SIMPLE: public PRIVILEGE_DATA{ /*~PROTOBEG~ PRIVILEGE_DATA_SIMPLE */ public: PRIVILEGE_DATA_SIMPLE (const char *line); PRIVILEGE_DATA_SIMPLE (void); int format_ascii (char *line); void setdialog (const char *title, DIALOG&dia); int validate (void); /*~PROTOEND~ PRIVILEGE_DATA_SIMPLE */ }; PUBLIC PRIVILEGE_DATA_SIMPLE::PRIVILEGE_DATA_SIMPLE(const char *line) { active = (char)atoi (line); } PUBLIC PRIVILEGE_DATA_SIMPLE::PRIVILEGE_DATA_SIMPLE() { active = 0; } /* format in ascii so the information may be stored in /etc/conf.linuxconf */ PUBLIC int PRIVILEGE_DATA_SIMPLE::format_ascii(char *line) { sprintf (line,"%d",active); return active; } /* Return 0 if the privilege is not granted to this users. Return 1 if the privilege is granted, but user must authenticate Return 2 if the privilege is granted, no question ask! */ PUBLIC int PRIVILEGE_DATA_SIMPLE::validate() { return active; } PUBLIC void PRIVILEGE_DATA_SIMPLE::setdialog(const char *title, DIALOG &dia) { first_field = dia.getnb(); static const char *tbopt[]={ MSG_U(O_DENY,"Denied"), MSG_U(O_PRIVENABLE,"Granted"), MSG_U(O_PRIVNOAUTH,"Granted/silent"), NULL }; dia.newf_chkm (title,active,tbopt); } PUBLIC PRIVILEGE_DATA *PRIVILEGE_DATAS::getitem(int no) { return (PRIVILEGE_DATA*)ARRAY::getitem(no); } /* A PRIVILEGE_DATA object to edit and control that kind of privilege. line contain raw info used to initialise the new object. */ PUBLIC VIRTUAL PRIVILEGE_DATA *PRIVILEGE::getdata (const char *user) { char key[100]; sprintf (key,"PRIVI_%s",id.get()); const char *line = linuxconf_getval (key,user); PRIVILEGE_DATA *ret = NULL; if (line == NULL){ ret = new PRIVILEGE_DATA_SIMPLE (); }else{ ret = new PRIVILEGE_DATA_SIMPLE (line); } return ret; } /* Save a PRIVILEGE_DATA object. Return -1 if any error */ PUBLIC VIRTUAL int PRIVILEGE::savedata ( const char *user, PRIVILEGE_DATA *data) { char line[1000]; int must_save = data->format_ascii (line); char key[100]; sprintf (key,"PRIVI_%s",id.get()); if (must_save){ linuxconf_replace (key,user,line); }else{ linuxconf_removeall (key,user); } return 0; } static int cmp_by_section(const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { PRIVILEGE *p1 = (PRIVILEGE*)o1; PRIVILEGE *p2 = (PRIVILEGE*)o2; int ret = strcmp(p1->getsection(),p2->getsection()); if (ret == 0) ret = strcmp(p1->gettitle(),p2->gettitle()); return ret; } /* Collect all PRIVILEGEs in an array and sort them Return the number collected. */ static int privilege_sort(PRIVILEGES &tbp) { PRIVILEGE *pt = first; while (pt != NULL){ tbp.add (pt); pt = pt->next; } tbp.sort (cmp_by_section); return tbp.getnb(); } void privilege_setdialog (DIALOG &dia, const char *user, PRIVILEGE_DATAS &tb) { PRIVILEGE_DECLARATOR *ptdecl = first_declarator; while (ptdecl != NULL){ ptdecl->fct(); ptdecl = ptdecl->next; } /* #Specification: privilege / presentation / order Privilege are presented in alphabetical order. First they are sort by section name and then internally in each section by title. A trick is provided to force some section before others. If a section title start with a digit, followed by a -, then this is used for the sort, but it is not displayed. When defining privileges in different section and you want to have those section is a specific order, then give the a title like # 1-this is the title. # Title are given in the declaration of a new privilege. The same mecanism apply to privilege title. */ PRIVILEGES tbp; int n = privilege_sort(tbp); const char *lastsection=""; for (int i=0; igetsection(); if (strcmp(section,lastsection)!=0){ const char *section_0 = section; if (section[0] != '\0' && section[1] == '-') section_0 +=2; dia.newf_title (section_0,2,"",section_0); lastsection = section; } PRIVILEGE_DATA *data = pt->getdata(user); const char *ptitle = pt->gettitle(); if (isdigit(ptitle[0]) && ptitle[1] == '-') ptitle +=2; data->setdialog (ptitle,dia); tb.add (data); } } /* Validate the different field in the dialog. Generate an error message. nof will contain the offending field return -1 if any error. */ int privilege_validate (PRIVILEGE_DATAS &, int &nof) { nof = 0; return 0; } /* Locate a PRIVILEGE object using its ID name Return NULL if not found. */ PRIVILEGE *privilege_lookup (const char *id) { PRIVILEGE *ret = NULL; PRIVILEGE *pt = first; while (pt != NULL){ if (pt->id.cmp(id)==0){ ret = pt; break; } pt = pt->next; } return ret; } int privilege_save(const char *user, PRIVILEGE_DATAS &tb, PRIVILEGE *priv) { linuxconf_setcursys (subsys_userpriv); PRIVILEGES tbp; int n = privilege_sort(tbp); for (int i=0; isavedata (user,data); } return linuxconf_save(priv); } PUBLIC PRIVILEGES::PRIVILEGES() { /* #Specification: PRIVILEGES / principle All PRIVILEGEs are static object. It is convenient to manage them in an array (PRIVILEGES). This array use the never_delete() mecanism to prevent deletion of the objects it contain. So a static PRIVILEGE may be store in a PRIVILEGES object without constraint. */ neverdelete(); } PUBLIC PRIVILEGE *PRIVILEGES::getitem (int no) { return (PRIVILEGE *)ARRAY::getitem(no); } PUBLIC PRIVI_FEATURE::PRIVI_FEATURE( const char *_id, // Key to store the privilege TRANS_NOTLOAD *_title, // Title shows in the user account dialog TRANS_NOTLOAD *_section)// Title of the section. All PRIVILEGE with // the same section title will be shown // together and the section title will be used // as a section separator in the dialog : PRIVILEGE (_id,_title,_section) { /* #Specification: PRIVI_FEATURE / principle A PRIVI_FEATURE is a PRIVILEGE which is not used to grant access but to control behavior. It is used generally only with the function perm_checkpriv() to see if a user may do or see something. There is no authentication checkbox associate with it. There is generally a related PRIVILEGE which is test in the application with perm_access(). For example, a PRIVILEGE may be defined to grant access to UUCP management and let the user control for example the schedule of calls and a PRIVI_FEATURE indicates if the user may see the full information, including the chat configuration (with password). */ } class PRIVILEGE_DATA_FEATURE: public PRIVILEGE_DATA_SIMPLE{ /*~PROTOBEG~ PRIVILEGE_DATA_FEATURE */ public: PRIVILEGE_DATA_FEATURE (const char *line); PRIVILEGE_DATA_FEATURE (void); void setdialog (const char *title, DIALOG&dia); /*~PROTOEND~ PRIVILEGE_DATA_FEATURE */ }; /* A PRIVILEGE_DATA object to edit and control that kind of privilege. line contain raw info used to initialise the new object. */ PUBLIC PRIVILEGE_DATA *PRIVI_FEATURE::getdata (const char *user) { char key[100]; sprintf (key,"PRIVI_%s",id.get()); const char *line = linuxconf_getval (key,user); PRIVILEGE_DATA_FEATURE *ret = NULL; if (line == NULL){ ret = new PRIVILEGE_DATA_FEATURE (); }else{ ret = new PRIVILEGE_DATA_FEATURE (line); } return ret; } PUBLIC void PRIVILEGE_DATA_FEATURE::setdialog(const char *title, DIALOG &dia) { first_field = dia.getnb(); dia.newf_chk (title,active,MSG_U(F_PRIVENABLE,"Granted")); } PUBLIC PRIVILEGE_DATA_FEATURE::PRIVILEGE_DATA_FEATURE(const char *line) : PRIVILEGE_DATA_SIMPLE (line) { } PUBLIC PRIVILEGE_DATA_FEATURE::PRIVILEGE_DATA_FEATURE() : PRIVILEGE_DATA_SIMPLE () { } // We setup a co-manager just to add a help screen class USERPRIVI_COMNG: public USERACCT_COMNG{ /*~PROTOBEG~ USERPRIVI_COMNG */ public: USERPRIVI_COMNG (DICTIONARY&_dict); int deluser (PRIVILEGE *); int save (PRIVILEGE *); void setupdia (DIALOG&dia); int validate (DIALOG&, int &); /*~PROTOEND~ USERPRIVI_COMNG */ }; PUBLIC USERPRIVI_COMNG::USERPRIVI_COMNG( DICTIONARY &_dict) : USERACCT_COMNG (_dict) { } static REGISTER_PRIVI_HELP *firsthelp; static USERCONF_HELP_FILE help_userprivi ("userprivi"); PUBLIC void USERPRIVI_COMNG::setupdia ( DIALOG &dia) { dia.addhelp (help_userprivi,MSG_U(T_USERPRIVI,"Privileges: introduction")); REGISTER_PRIVI_HELP *pt = firsthelp; while (pt != NULL){ dia.addhelp (pt->help,pt->title->get()); pt = pt->next; } } PUBLIC int USERPRIVI_COMNG::save( PRIVILEGE *) { return 0; } PUBLIC int USERPRIVI_COMNG::validate( DIALOG &, int &) { return 0; } PUBLIC int USERPRIVI_COMNG::deluser ( PRIVILEGE *) { return 0; } /* The purpose of this object is to associate a help file and a title to the user account dialog. The help file is generally related to privileges. The idea is that the various parts which define privilege do not participate directly in the user account dialog yet, the privilege they define end up there. Using this mecanism, they can document the privileges in a help file and make it visible in the user account dialog. This is done simply by declaring a static object of type REGISTER_PRIVI_HELP like this: # static HELP_FILE help ("module","subject"); static REGISTER_PRIVI_HELP p(help,P_MSG_U(T_THISHELP,"bla bla bla")); # */ PUBLIC REGISTER_PRIVI_HELP::REGISTER_PRIVI_HELP( HELP_FILE &_help, TRANS_NOTLOAD *_title) : help(_help) { next = firsthelp; firsthelp = this; title = _title; } // #Specbeg: USERACCT_COMNG / registration / sample /* This function defines a co-manager only for a user account dialog (key == "user"). Further, using the DICTIONARY object, it gets more information about the dialog. It creates the co-manager only for normal user account in the main domain. */ USERACCT_COMNG *privi_newcomng( const char *key, DICTIONARY &dict) { USERACCT_COMNG *ret = NULL; const char *domain = dict.get_str ("domain"); int categ = dict.get_int ("categ"); if (strcmp(key,"user")==0 && strcmp(domain,"/")==0 && categ == TUSER_STD){ ret = new USERPRIVI_COMNG (dict); } return ret; } static REGISTER_USERACCT_COMNG ppriv (privi_newcomng); // #Specend: static USERCONF_HELP_FILE help_genprivi ("genprivi"); static REGISTER_PRIVI_HELP p2 (help_genprivi ,P_MSG_U(T_GENPRIVI,"Privileges: General system control")); static USERCONF_HELP_FILE help_useradmprivi ("useradmprivi"); static REGISTER_PRIVI_HELP p3 (help_useradmprivi ,P_MSG_U(T_USERADMPRIVI,"Privileges: User accounts"));