#include #include #include #include #include #include #include "userconf.h" #include "internal.h" #include "userconf.m" #include #include #include static time_t lasttime_root = 0; static time_t lasttime_user = 0; static PRIVILEGE p_rootequiv ("rootequiv" ,P_MSG_U(T_PRIVROOTEQUIV,"SuperUser equivalence") ,P_MSG_U(T_PSYSCONTROL,"0-General system control")); static int (*perm_fct_check) (const char *user); static int (*perm_fct_check_pair) (const char *user, const char *passwd); int (*perm_fct_change) ( const char *user, bool pre_authenticated, const char *suggested, void (*fct_html) (M_DIALOG &dia, bool intro)); static const char *argsauth[]={ "pseudo-user","password",NULL }; static const char *argspriv[]={ "pseudo-user","privilege*",NULL }; static MESSAGE_DEF checkauth ("coadminauth",argsauth); static MESSAGE_DEF checkpriv ("coadminpriv",argspriv); /* Register override function for authentication and password management This is normally called by distribution specific module (only one can call this function in a given linuxconf session). It was created to allow linuxconf to support PAM and still operate normally on non-PAM system. */ void passwd_sethook ( int (*_fct_check)(const char *user), int (*_fct_change) (const char *user, bool pre_authenticated, const char *suggested, void (*_fct_html)(M_DIALOG &dia, bool intro)), int (*_fct_check_pair)(const char *user, const char *passwd)) { perm_fct_check = _fct_check; perm_fct_check_pair = _fct_check_pair; perm_fct_change = _fct_change; } /* Force a one minute "no question asked" root autorization for one minute. Normally linuxconf does nothing priviledged unless the user has provided some password (generally root). Some operation are indeed priviledge but are accepted, for example, changing his/her password. */ void perm_forceok() { lasttime_root = lasttime_user = time(&lasttime_root); } #define VALIDATION_TIME (2*60) /* Get the crypt password of a user */ static int perm_getupass ( USERS &users, const char *username, char password[]) { /* #Specification: password / strategy / by hand To support transparently standard and shadow password without recompiling, linuxconf read manually the /etc/passwd and /etc/shadow files. This is causing problem for NIS user though. This will have to be fixed. */ USER *usr = users.getitem(username); int ret = -1; if (usr != NULL){ SHADOW *shadow = users.getshadow(usr); if (!usr->is_locked(shadow)){ const char *pwd = usr->getpwd(); if (shadow != NULL){ pwd = shadow->getpwd(); } strcpy (password,pwd); ret = 0; } } return ret; } /* Verify if a user/password combination is valid */ bool perm_validpass ( USERS &users, // Could be a user list of a virtual domain const char *username, const char *pass) { bool ret = false; char crypt_pass[100]; if (perm_getupass(users,username,crypt_pass)!=-1 && strcmp(crypt(pass, crypt_pass),crypt_pass)==0){ ret = true; } return ret; } /* Verify if a user/password combination is valid */ int perm_validpass (const char *username, const char *pass) { int ret = 0; if (perm_fct_check_pair != NULL){ ret = (*perm_fct_check_pair)(username,pass); }else{ USERS users; ret = perm_validpass (users,username,pass); } return ret; } static int html_uid=-1; static SSTRING html_user; const int PERM_HTML_PSEUDOADMIN=1; /* Sent by the html mode to identify the user. */ void perm_setaccess (const char *username, const char *password) { html_uid = -1; const char *args[]={username,password,NULL}; html_user.setfrom (username); if (module_sendmessage (checkauth,2,args)==1){ html_uid = PERM_HTML_PSEUDOADMIN; }else{ struct passwd *pwd = getpwnam (username); if (pwd != NULL){ if (perm_fct_check_pair != NULL){ if ((*perm_fct_check_pair)(username,password)){ html_uid = pwd->pw_uid; } }else{ USERS users; USER *user = users.getitem(username); char upass[100]; if (user != NULL && perm_getupass(users,username,upass)!=-1){ if (upass[0] != '\0'){ if (strcmp(crypt(password, upass),upass)==0){ html_uid = user->getuid(); } }else if (password[0] == '\0'){ html_uid = user->getuid(); } } } } } } static int perm_checkhook ( const char *uname) { int ret = 0; for (int i=0; i<3; i++){ ret = (*perm_fct_check)(uname); if (ret != 0){ break; }else{ xconf_error (MSG_U(E_IVLDPASS,"Invalid password")); } } return ret; } /* Ask the password for root or optionnally the user. Return 1 if a good password was supplied */ static int perm_askpass ( USERS &users, const char *uname) { int ret = 0; if (perm_fct_check != NULL){ if (uname != NULL){ ret = perm_checkhook(uname); if (ret == -1){ ret = perm_checkhook("root"); if (ret == 1){ lasttime_root = time(NULL)+ VALIDATION_TIME; }else{ ret = 0; } }else if (ret == 1){ lasttime_user = time(NULL)+ VALIDATION_TIME; } }else{ ret = perm_checkhook("root"); if (ret == 1){ lasttime_root = time(NULL)+ VALIDATION_TIME; }else{ ret = 0; } } }else{ /* #Specification: root access / password validation When the admin/user must provide the root password, he has 3 chances. */ struct { char root[100]; char user[100]; }crypt_pass; if (perm_getupass (users,"root",crypt_pass.root) == -1){ xconf_error (MSG_U(E_NOROOT ,"No root user defined in /etc/passwd\n" "Can't let you in!")); ret = 0; }else if (crypt_pass.root[0] != '\0'){ if (uname != NULL && perm_getupass (users,uname,crypt_pass.user) == -1){ xconf_error (MSG_U(E_NOUSER ,"No user %s in /etc/passwd\n" "privilege denied") ,uname); } for (int i=0; i<3; i++){ DIALOG dia; SSTRING rootpass,userpass; dia.newf_pass (MSG_U(F_ROOTPASS,"root password"),rootpass); const char *intro = MSG_U(I_ENTERPASS ,"Enter password for root\n" "Only the superuser is allowed to perform\n" "configuration task."); if (uname != NULL){ dia.newf_pass (MSG_U(F_YOURPASS,"Your password"),userpass); intro = MSG_U(I_ENTERONEPASS ,"Enter password for root or your password\n" "The superuser and you are allowed to perform\n" "this configuration task."); } if (dia.edit_form (MSG_U(T_PASSREQ,"Password required") ,intro ,help_nil) != MENU_ACCEPT){ ret = 0; break; }else if (uname != NULL && strcmp(crypt(userpass.get(), crypt_pass.user) ,crypt_pass.user)==0){ lasttime_user = time(NULL)+ VALIDATION_TIME; ret = 1; break; }else if (strcmp(crypt(rootpass.get(), crypt_pass.root) ,crypt_pass.root)!=0){ ret = 0; xconf_error (MSG_R(E_IVLDPASS)); }else{ lasttime_root = time(NULL)+ VALIDATION_TIME; ret = 1; break; } } } } return ret; } static int perm_privok (PRIVILEGE *priv, USER *user, bool &mustident) { int ret = 0; mustident = true; if (user != NULL){ const char *name = user->getname(); int mustequiv = 1, mustpriv = 1; { PRIVILEGE_DATA *edata = p_rootequiv.getdata (name); ret = edata->has_priv(); if (ret) mustequiv = edata->mustident(); delete edata; } /* We must always check the privilege even if this user has root equivalence privilege. This is because we must know the mustident flag. A user may have the root privilege with "must identify" status, while he may have also a more specific privilege "granted/silent". */ if (priv != NULL){ PRIVILEGE_DATA *data = priv->getdata (name); int retpriv = data->has_priv(); if (retpriv){ mustpriv = data->mustident(); ret = 1; } delete data; } if (mustequiv == 0 || mustpriv == 0) mustident = false; } return ret; } static int perm_privok (PRIVILEGE *priv, USER *user) { bool mustident; return perm_privok(priv,user,mustident); } /* Verify if there is password for root. If so ask the user to enter it and validate it. So askrunlevel is "safe". No one will be allowed to reconfigured the network if he don't know the root password. Return != 0 if user if allowed to get in. If priv != NULL, the user password is also checked for. */ static int perm_checkpass (PRIVILEGE *priv) { time_t curtime = time(NULL); /* #Specification: root access / timeout When the user select a configuration task, the password for root must be supplied. This "validation" is good for 2 minute. It means that the user may do several configuration in one minutes without being asked for the root password every time. If the user wait a minute or more, the password will be asked again. Look safe and user friendly to me. */ int ret = 1; if (dialog_mode == DIALOG_HTML){ if (html_uid == -1){ html_needpasswd(); ret = 0; }else if (html_uid == PERM_HTML_PSEUDOADMIN){ // Pseudo user, ask the module if he has the privilege const char *args[]={html_user.get(),(char*)priv,NULL}; ret = module_sendmessage (checkpriv,2,args); }else if (html_uid != 0){ USERS users; USER *user = users.getfromuid (html_uid); if (!perm_privok(priv,user)){ html_needpasswd(); ret = 0; } } }else if (curtime - lasttime_root > VALIDATION_TIME){ USERS users; USER *user = users.getfromuid (getuid()); if (user == NULL){ xconf_error (MSG_U(E_NOUSERFROMID ,"Can't identify you from your user ID (%d)") ,getuid()); ret = perm_askpass(users,NULL); }else{ bool mustident; if (perm_privok(priv,user,mustident)){ if (mustident && curtime - lasttime_user > VALIDATION_TIME){ ret = perm_askpass (users,user->getname()); } }else{ ret = perm_askpass (users,NULL); } } } return ret; } /* Verify if there is password for root. If so ask the user to enter it and validate it. So askrunlevel is "safe". No one will be allowed to reconfigured the network if he don't know the root password. Return != 0 if user if allowed to get in. */ int perm_checkpass () { return perm_checkpass (NULL); } static bool perm_html_mode = false; /* Record the current default privilege. Return the current one (which is normally NULL). */ PRIVILEGE *perm_setdefprivi (PRIVILEGE *priv) { PRIVILEGE *old = ui_context.defpriv; ui_context.defpriv = priv; return old; } /* Return the current default privilege (usually NULL) */ PRIVILEGE *perm_getdefprivi () { return ui_context.defpriv; } static bool bypass = 0; /* Disconnect privilege management. Everything is allowed if b is true */ void perm_setbypass(bool b) { bypass = b; } /* Check if the user is really root. If it is not, but the effective ID is root, then ask for the root password. Return != 0 if the real UID is root or the user knows the root password. It prints an informative message about the action which will occur. */ static int perm_v_access(PRIVILEGE *priv, const char *buf) { if (dialog_mode == DIALOG_TREE) return 0; if (bypass) return 1; /* #Specification: privileges / default privilege If a default privilege is defined, it overrides any privileges. */ if (ui_context.defpriv != NULL) priv = ui_context.defpriv; /* #Specification: configurator / setuid root The configurator (anything-conf) can be set setuid root. It will allows normal users to get in and then will ask for the root password at the proper time. This strategy make the system friendlier. It allows normal user to inspect the configuration (if allowed) and when finding something odd, use the root password (if known) to fix things, The idea here is that we generally think first about getting somewhere and later about the permissions needed to get there. The nice thing about this scheme is that this program will deny root access automaticly after some time. If you don't like this, then don't set it setuid root. It will operate correctly and won't bug you with password. */ int ret = perm_html_mode ? 0 : getuid()==0; if (geteuid()==0){ if (!ret){ ret = perm_checkpass(priv); } }else{ xconf_error (MSG_U(E_MUSTBEROOT,"You must be root to\n%s"),buf); } return ret; } /* Check if the user is really root. If it is not, but the effective ID is root, then ask for the root password. Return != 0 if the real UID is root or the user knows the root password. It prints an informative message about the action which will occur. */ int perm_rootaccess(const char *ctl, ...) { va_list list; va_start (list,ctl); char buf[1000]; vsprintf (buf,ctl,list); va_end (list); return perm_v_access (NULL,buf); } /* Check if the user is really root or has the privilege to do a task If it is not, but the effective ID is root, then ask for the root password or his own password. Return != 0 if the real UID is root or the user knows the root password or has enough privilege. It prints an informative message about the action which will occur. */ int perm_access(PRIVILEGE *priv, const char *ctl, ...) { va_list list; va_start (list,ctl); char buf[1000]; vsnprintf (buf,sizeof(buf)-1,ctl,list); va_end (list); return perm_v_access (priv,buf); } /* Check if the user has a privilege. If the user has supplied the root password once, it has the privilege Return != 0 if so. */ int perm_checkpriv (PRIVILEGE *priv) { int ret = 0; if (perm_html_mode){ if (html_uid == -1){ html_needpasswd(); }else if (html_uid == PERM_HTML_PSEUDOADMIN){ // Pseudo user, ask the module if he has the privilege const char *args[]={html_user.get(),(char*)priv,NULL}; ret = module_sendmessage (checkpriv,2,args); }else{ USERS users; ret = perm_privok (priv,users.getfromuid (html_uid)); } }else{ if (lasttime_root > 0 || getuid()==0){ ret = 1; }else{ USERS users; ret = perm_privok (priv,users.getfromuid (getuid())); } } return ret; } /* Get user ID operating linuxconf currrently */ int perm_getadminuid() { int ret = getuid(); if (perm_html_mode){ ret = html_uid; } return ret; } /* get the authenticated user ID. */ int perm_getuid() { int ret = perm_getadminuid(); if (time(NULL) - lasttime_root < VALIDATION_TIME){ ret = 0; } return ret; } /* Set the html mode for granting priviledge. In html mode we are executing as root all the time. Normally no question is asked to root. Now we must ask for password before going through. */ void perm_sethtml (bool _mode) { perm_html_mode = _mode; }