You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeadmin/kuser/kuserfiles.cpp

621 lines
16 KiB

/*
* Copyright (c) 1998 Denis Perchine <dyp@perchine.com>
* Copyright (c) 2004 Szombathelyi György <gyurco@freemail.hu>
* Former maintainer: Adriaan de Groot <groot@kde.org>
*
* 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 <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SHADOW
#include <shadow.h>
#endif
#include <tqstring.h>
#include <tqdir.h>
#include "kglobal_.h"
#include "kuserfiles.h"
#include "misc.h"
#include <kstandarddirs.h>
#include <kmessagebox.h>
#include <kdebug.h>
#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.").tqarg(mCfg->passwdsrc().tqarg(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.").tqarg(filename).tqarg(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.").tqarg(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.").tqarg(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.").tqarg(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.").tqarg(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.").tqarg(nispasswd_filename) );
}
TQPtrListIterator<KU::KUser> it( mUsers );
KU::KUser *user;
bool addok = false;
user = (*it);
while (true) {
if ( user == 0 ) {
if ( addok ) break;
it = TQPtrListIterator<KU::KUser> ( 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.").tqarg(new_shadow_file) );
return FALSE;
}
s.sp_namp = (char *)malloc(200);
s.sp_pwdp = (char *)malloc(200);
TQPtrListIterator<KU::KUser> it( mUsers );
up = (*it);
while (true) {
if ( up == 0 ) {
if ( addok ) break;
it = TQPtrListIterator<KU::KUser> ( 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;
}