/* * Copyright (c) 1998 Denis Perchine * Copyright (c) 2004 Szombathelyi György * Former maintainer: Adriaan de Groot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "globals.h" #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #ifdef HAVE_SHADOW #include #endif #include #include #include "kglobal_.h" #include "kuserfiles.h" #include "misc.h" #include #include #include #include "editDefaults.h" KUserFiles::KUserFiles(KUserPrefsBase *cfg) : KUsers( cfg ) { pw_backuped = FALSE; pn_backuped = FALSE; s_backuped = FALSE; pwd_mode = 0644; pwd_uid = 0; pwd_gid = 0; sdw_mode = 0600; sdw_uid = 0; sdw_gid = 0; mUsers.setAutoDelete(TRUE); caps = Cap_Passwd; #ifdef HAVE_SHADOW if ( !mCfg->shadowsrc().isEmpty() ) caps |= Cap_Shadow; #endif #if defined(__FreeBSD__) || defined(__bsdi__) caps |= Cap_BSD; #endif reload(); } KUserFiles::~KUserFiles() { } bool KUserFiles::reload() { if (!loadpwd()) return FALSE; if (!loadsdw()) return FALSE; return TRUE; } // Load passwd file bool KUserFiles::loadpwd() { passwd *p; KU::KUser *tmpKU = 0; struct stat st; TQString filename; TQString passwd_filename; TQString nispasswd_filename; int rc = 0; int passwd_errno = 0; int nispasswd_errno = 0; char processing_file = '\0'; #define P_PASSWD 0x01 #define P_NISPASSWD 0x02 #define MAXFILES 2 // Read KUser configuration passwd_filename = mCfg->passwdsrc(); nispasswd_filename = mCfg->nispasswdsrc(); // Handle unconfigured environments if(passwd_filename.isEmpty() && nispasswd_filename.isEmpty()) { mCfg->setPasswdsrc( PASSWORD_FILE ); mCfg->setGroupsrc( GROUP_FILE ); passwd_filename = mCfg->passwdsrc(); KMessageBox::error( 0, i18n("KUser sources were not configured.\nLocal passwd source set to %1\nLocal group source set to %2.").arg(mCfg->passwdsrc().arg(mCfg->groupsrc())) ); } if(!passwd_filename.isEmpty()) { processing_file = processing_file | P_PASSWD; filename.append(passwd_filename); } // Start reading passwd file(s) for(int i = 0; i < MAXFILES; i++) { rc = stat(TQFile::encodeName(filename), &st); if(rc != 0) { KMessageBox::error( 0, i18n("Stat call on file %1 failed: %2\nCheck KUser settings.").arg(filename).arg(TQString::fromLocal8Bit(strerror(errno))) ); if( (processing_file & P_PASSWD) != 0 ) { passwd_errno = errno; if(!nispasswd_filename.isEmpty()) { processing_file = processing_file & ~P_PASSWD; processing_file = processing_file | P_NISPASSWD; filename.truncate(0); filename.append(nispasswd_filename); } continue; } else{ nispasswd_errno = errno; break; } } pwd_mode = st.st_mode & 0666; pwd_uid = st.st_uid; pwd_gid = st.st_gid; // We are reading our configuration specified passwd file TQString tmp; #ifdef HAVE_FGETPWENT FILE *fpwd = fopen(TQFile::encodeName(filename), "r"); if(fpwd == NULL) { KMessageBox::error( 0, i18n("Error opening %1 for reading.").arg(filename) ); return FALSE; } while ((p = fgetpwent(fpwd)) != NULL) { #else setpwent(); //This should be enough for BSDs while ((p = getpwent()) != NULL) { #endif tmpKU = new KU::KUser(); tmpKU->setCaps( KU::KUser::Cap_POSIX ); tmpKU->setUID(p->pw_uid); tmpKU->setGID(p->pw_gid); tmpKU->setName(TQString::fromLocal8Bit(p->pw_name)); tmp = TQString::fromLocal8Bit( p->pw_passwd ); if ( tmp != "x" && tmp != "*" && !tmp.startsWith("!") ) tmpKU->setDisabled( false ); else tmpKU->setDisabled( true ); if ( tmp.startsWith("!") ) tmp.remove(0, 1); tmpKU->setPwd( tmp ); tmpKU->setHomeDir(TQString::fromLocal8Bit(p->pw_dir)); tmpKU->setShell(TQString::fromLocal8Bit(p->pw_shell)); #if defined(__FreeBSD__) || defined(__bsdi__) tmpKU->setClass(TQString::fromLatin1(p->pw_class)); tmpKU->setLastChange(p->pw_change); tmpKU->setExpire(p->pw_expire); #endif if ((p->pw_gecos != 0) && (p->pw_gecos[0] != 0)) fillGecos(tmpKU, p->pw_gecos); mUsers.append(tmpKU); } // End reading passwd_filename #ifdef HAVE_FGETPWENT fclose(fpwd); #else endpwent(); #endif if((!nispasswd_filename.isEmpty()) && (nispasswd_filename != passwd_filename)) { processing_file = processing_file & ~P_PASSWD; processing_file = processing_file | P_NISPASSWD; filename.truncate(0); filename.append(nispasswd_filename); } else break; } // end of processing files, for loop if( (passwd_errno == 0) && (nispasswd_errno == 0) ) return (TRUE); if( (passwd_errno != 0) && (nispasswd_errno != 0) ) return (FALSE); else return(TRUE); } // Load shadow passwords bool KUserFiles::loadsdw() { #ifdef HAVE_SHADOW TQString shadow_file,tmp; struct spwd *spw; KU::KUser *up = NULL; struct stat st; shadow_file = mCfg->shadowsrc(); if ( shadow_file.isEmpty() ) return TRUE; stat( TQFile::encodeName(shadow_file), &st); sdw_mode = st.st_mode & 0666; sdw_uid = st.st_uid; sdw_gid = st.st_gid; #ifdef HAVE_FGETSPENT FILE *f; kdDebug() << "open shadow file: " << shadow_file << endl; if ((f = fopen( TQFile::encodeName(shadow_file), "r")) == NULL) { KMessageBox::error( 0, i18n("Error opening %1 for reading.").arg(shadow_file) ); caps &= ~Cap_Shadow; return TRUE; } while ((spw = fgetspent( f ))) { // read a shadow password structure #else setspent(); while ((spw = getspent())) { // read a shadow password structure #endif kdDebug() << "shadow entry: " << spw->sp_namp << endl; if ((up = lookup(TQString::fromLocal8Bit(spw->sp_namp))) == NULL) { KMessageBox::error( 0, i18n("No /etc/passwd entry for %1.\nEntry will be removed at the next `Save'-operation.").arg(TQString::fromLocal8Bit(spw->sp_namp)) ); continue; } tmp = TQString::fromLocal8Bit( spw->sp_pwdp ); if ( tmp.startsWith("!!") || tmp == "*" ) { up->setDisabled( true ); tmp.remove( 0, 2 ); } else up->setDisabled( false ); up->setSPwd( tmp ); // cp the encrypted pwd up->setLastChange( daysToTime( spw->sp_lstchg ) ); up->setMin(spw->sp_min); up->setMax(spw->sp_max); #ifndef _SCO_DS up->setWarn(spw->sp_warn); up->setInactive(spw->sp_inact); up->setExpire( daysToTime( spw->sp_expire ) ); up->setFlag(spw->sp_flag); #endif } #ifdef HAVE_FGETSPENT fclose(f); #else endspent(); #endif #endif // HAVE_SHADOW return TRUE; } // Save password file #define escstr(a,b) tmp2 = user->a(); \ tmp2.replace(':',"_"); \ tmp2.replace(',',"_"); \ user->b( tmp2 ); bool KUserFiles::savepwd() { FILE *passwd_fd = NULL; FILE *nispasswd_fd = NULL; uid_t minuid = 0; int nis_users_written = 0; uid_t tmp_uid = 0; TQString s; TQString s1; TQString tmp, tmp2; TQString passwd_filename; TQString nispasswd_filename; char errors_found = '\0'; #define NOMINUID 0x01 #define NONISPASSWD 0x02 // Read KUser configuration info passwd_filename = mCfg->passwdsrc(); nispasswd_filename = mCfg->nispasswdsrc(); TQString new_passwd_filename = passwd_filename + TQString::fromLatin1(KU_CREATE_EXT); TQString new_nispasswd_filename = nispasswd_filename+TQString::fromLatin1(KU_CREATE_EXT); if( nispasswd_filename != passwd_filename ) { minuid = mCfg->nisminuid(); } // Backup file(s) if(!passwd_filename.isEmpty()) { if (!pw_backuped) { if (!backup(passwd_filename)) return FALSE; pw_backuped = TRUE; } } if(!nispasswd_filename.isEmpty() && (nispasswd_filename != passwd_filename)) { if (!pn_backuped) { if (!backup(nispasswd_filename)) return FALSE; pn_backuped = TRUE; } } // Open file(s) if(!passwd_filename.isEmpty()) { if ((passwd_fd = fopen(TQFile::encodeName(new_passwd_filename),"w")) == NULL) KMessageBox::error( 0, i18n("Error opening %1 for writing.").arg(passwd_filename) ); } if(!nispasswd_filename.isEmpty() && (nispasswd_filename != passwd_filename)){ if ((nispasswd_fd = fopen(TQFile::encodeName(new_nispasswd_filename),"w")) == NULL) KMessageBox::error( 0, i18n("Error opening %1 for writing.").arg(nispasswd_filename) ); } TQPtrListIterator it( mUsers ); KU::KUser *user; bool addok = false; user = (*it); while (true) { if ( user == 0 ) { if ( addok ) break; it = TQPtrListIterator ( mAdd ); user = (*it); addok = true; if ( user == 0 ) break; }; if ( mDel.containsRef( user ) ) { ++it; user = (*it); continue; } if ( mMod.contains( user ) ) user = &( mMod[ user ] ); tmp_uid = user->getUID(); if ( caps & Cap_Shadow ) tmp = "x"; else { tmp = user->getPwd(); if ( user->getDisabled() && tmp != "x" && tmp != "*" ) tmp = "!" + tmp; } escstr( getName, setName ); escstr( getHomeDir, setHomeDir ); escstr( getShell, setShell ); escstr( getName, setName ); escstr( getFullName, setFullName ); #if defined(__FreeBSD__) || defined(__bsdi__) escstr( getClass, setClass ); escstr( getOffice, setOffice ); escstr( getWorkPhone, setWorkPhone ); escstr( getHomePhone, setHomePhone ); s = user->getName() + ":" + tmp + ":" + TQString::number( user->getUID() ) + ":" + TQString::number( user->getGID() ) + ":" + user->getClass() + ":" + TQString::number( user->getLastChange() ) + ":" + TQString::number( user->getExpire() ) + ":"; s1 = user->getFullName() + "," + user->getOffice() + "," + user->getWorkPhone() + "," + user->getHomePhone(); #else escstr( getOffice1, setOffice1 ); escstr( getOffice2, setOffice2 ); escstr( getAddress, setAddress ); s = user->getName() + ":" + tmp + ":" + TQString::number( user->getUID() ) + ":" + TQString::number( user->getGID() ) + ":"; s1 = user->getFullName() + "," + user->getOffice1() + "," + user->getOffice2() + "," + user->getAddress(); #endif for (int j=(s1.length()-1); j>=0; j--) { if (s1[j] != ',') break; s1.truncate(j); } s += s1 + ":" + user->getHomeDir() + ":" + user->getShell() + "\n"; if( (nispasswd_fd != 0) && (minuid != 0) ) { if (minuid <= tmp_uid) { fputs(s.local8Bit().data(), nispasswd_fd); nis_users_written++; ++it; user = (*it); continue; } } if( (nispasswd_fd != 0) && (minuid == 0) ) { errors_found = errors_found | NOMINUID; } if( (nispasswd_fd == 0) && (minuid != 0) ) { errors_found = errors_found | NONISPASSWD; } kdDebug() << s << endl; fputs(s.local8Bit().data(), passwd_fd); ++it; user = (*it); } if(passwd_fd) { fclose(passwd_fd); chmod(TQFile::encodeName(new_passwd_filename), pwd_mode); chown(TQFile::encodeName(new_passwd_filename), pwd_uid, pwd_gid); rename(TQFile::encodeName(new_passwd_filename), TQFile::encodeName(passwd_filename)); } if(nispasswd_fd) { fclose(nispasswd_fd); chmod(TQFile::encodeName(new_nispasswd_filename), pwd_mode); chown(TQFile::encodeName(new_nispasswd_filename), pwd_uid, pwd_gid); rename(TQFile::encodeName(new_nispasswd_filename), TQFile::encodeName(nispasswd_filename)); } if( (errors_found & NOMINUID) != 0 ) { KMessageBox::error( 0, i18n("Unable to process NIS passwd file without a minimum UID specified.\nPlease update KUser settings (Files).") ); } if( (errors_found & NONISPASSWD) != 0 ) { KMessageBox::error( 0, i18n("Specifying NIS minimum UID requires NIS file(s).\nPlease update KUser settings (Files).") ); } // need to run a utility program to build /etc/passwd, /etc/pwd.db // and /etc/spwd.db from /etc/master.passwd #if defined(__FreeBSD__) || defined(__bsdi__) if (system(PWMKDB) != 0) { KMessageBox::error( 0, i18n("Unable to build password database.") ); return FALSE; } #else if( (nis_users_written > 0) || (nispasswd_filename == passwd_filename) ) { if (system(PWMKDB) != 0) { KMessageBox::error( 0, i18n("Unable to build password databases.") ); return FALSE; } } #endif return TRUE; } #undef escstr // Save shadow passwords file bool KUserFiles::savesdw() { #ifdef HAVE_SHADOW bool addok = false; TQString tmp; FILE *f; struct spwd *spwp; struct spwd s; KU::KUser *up; TQString shadow_file = mCfg->shadowsrc(); TQString new_shadow_file = shadow_file+TQString::fromLatin1(KU_CREATE_EXT); if ( shadow_file.isEmpty() ) return TRUE; if (!s_backuped) { if (!backup(shadow_file)) return FALSE; s_backuped = TRUE; } if ((f = fopen(TQFile::encodeName(new_shadow_file), "w")) == NULL) { KMessageBox::error( 0, i18n("Error opening %1 for writing.").arg(new_shadow_file) ); return FALSE; } s.sp_namp = (char *)malloc(200); s.sp_pwdp = (char *)malloc(200); TQPtrListIterator it( mUsers ); up = (*it); while (true) { if ( up == 0 ) { if ( addok ) break; it = TQPtrListIterator ( mAdd ); up = (*it); addok = true; if ( up == 0 ) break; }; if ( mDel.containsRef( up ) ) { ++it; up = (*it); continue; } if ( mMod.contains( up ) ) up = &( mMod[ up ] ); strncpy( s.sp_namp, up->getName().local8Bit(), 200 ); if ( up->getDisabled() ) strncpy( s.sp_pwdp, TQString("!!" + up->getSPwd()).local8Bit(), 200 ); else strncpy( s.sp_pwdp, up->getSPwd().local8Bit(), 200 ); s.sp_lstchg = timeToDays( up->getLastChange() ); s.sp_min = up->getMin(); s.sp_max = up->getMax(); #ifndef _SCO_DS s.sp_warn = up->getWarn(); s.sp_inact = up->getInactive(); s.sp_expire = timeToDays( up->getExpire() ); s.sp_flag = up->getFlag(); #endif spwp = &s; putspent(spwp, f); ++it; up = (*it); } fclose(f); chmod(TQFile::encodeName(new_shadow_file), sdw_mode); chown(TQFile::encodeName(new_shadow_file), sdw_uid, sdw_gid); rename(TQFile::encodeName(new_shadow_file), TQFile::encodeName(shadow_file)); free(s.sp_namp); free(s.sp_pwdp); #endif // HAVE_SHADOW return TRUE; } void KUserFiles::createPassword( KU::KUser *user, const TQString &password ) { if ( caps & Cap_Shadow ) { user->setSPwd( encryptPass( password, mCfg->md5shadow() ) ); user->setPwd( TQString::fromLatin1("x") ); } else user->setPwd( encryptPass( password, false ) ); } bool KUserFiles::dbcommit() { bool ret; mode_t mode; mAddSucc.clear(); mDelSucc.clear(); mModSucc.clear(); if ( mDel.isEmpty() && mAdd.isEmpty() && mMod.isEmpty() ) return true; mode = umask(0077); ret = savepwd(); if ( ret && ( caps & Cap_Shadow ) ) ret = savesdw(); umask( mode ); if ( !ret ) return false; mDelSucc = mDel; mAddSucc = mAdd; mModSucc = mMod; mDel.clear(); mAdd.clear(); mMod.clear(); return TRUE; }