Commit 1d995cd7 authored by jan.koester's avatar jan.koester
Browse files

test

parent 00457c7f
Loading
Loading
Loading
Loading
+39 −82
Changes for commands/auth/authutil.h: 39 added lines, 82 removed lines.
Original line number Diff line number Diff line
@@ -25,19 +25,23 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

// Shared /etc/passwd + /etc/shadow lookup, password prompting and crypt(3)
// verification for login/su/passwd/getty -- kept in one place rather than
// duplicated per command (unlike the ELF .modinfo reader in modinfo.cpp/
// depmod.cpp) since a subtle inconsistency here is a real security bug, not
// just a formatting difference.
// Shared account lookup, password prompting and crypt(3) verification for
// login/su/passwd/getty -- kept in one place rather than duplicated per
// command (unlike the ELF .modinfo reader in modinfo.cpp/depmod.cpp) since a
// subtle inconsistency here is a real security bug, not just a formatting
// difference.
//
// Goes through the real libc account database (getpwnam()/getpwuid(),
// backed by /etc/user.yaml; getspnam()/shadow_update_hash(), backed by
// /etc/shadow.yaml) instead of parsing /etc/passwd and /etc/shadow itself --
// one real account database for every consumer (whoami, id, ls -l's owner
// names, ...), not a second, independently-parsed copy of it just for these
// four commands.

#pragma once

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@@ -46,6 +50,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <termios.h>
#include <crypt.h>
#include <sys/types.h>
#include <pwd.h>
#include <shadow.h>

namespace fresh { namespace auth {

@@ -55,58 +61,39 @@ namespace fresh { namespace auth {
        gid_t gid=0;
    };

    inline std::vector<std::string> splitColon(const std::string &line){
        std::vector<std::string> f;
        std::istringstream iss(line);
        std::string tok;
        while(std::getline(iss,tok,':')) f.push_back(tok);
        return f;
    inline void fillFromPasswd(const struct passwd *pw,PasswdEntry &out){
        out.name=pw->pw_name;
        out.passwd=pw->pw_passwd;
        out.uid=pw->pw_uid;
        out.gid=pw->pw_gid;
        out.gecos=pw->pw_gecos;
        out.home=pw->pw_dir;
        out.shell=pw->pw_shell;
    }

    inline bool lookupPasswd(const std::string &username,PasswdEntry &out){
        std::ifstream in("/etc/passwd");
        std::string line;
        while(std::getline(in,line)){
            std::vector<std::string> f=splitColon(line);
            if(f.size()<7 || f[0]!=username) continue;
            out.name=f[0]; out.passwd=f[1];
            out.uid=(uid_t)std::strtoul(f[2].c_str(),nullptr,10);
            out.gid=(gid_t)std::strtoul(f[3].c_str(),nullptr,10);
            out.gecos=f[4]; out.home=f[5]; out.shell=f[6];
        struct passwd *pw=getpwnam(username.c_str());
        if(!pw) return false;
        fillFromPasswd(pw,out);
        return true;
    }
        return false;
    }

    inline bool lookupPasswdByUid(uid_t uid,PasswdEntry &out){
        std::ifstream in("/etc/passwd");
        std::string line;
        while(std::getline(in,line)){
            std::vector<std::string> f=splitColon(line);
            if(f.size()<7) continue;
            if((uid_t)std::strtoul(f[2].c_str(),nullptr,10)!=uid) continue;
            out.name=f[0]; out.passwd=f[1]; out.uid=uid;
            out.gid=(gid_t)std::strtoul(f[3].c_str(),nullptr,10);
            out.gecos=f[4]; out.home=f[5]; out.shell=f[6];
        struct passwd *pw=getpwuid(uid);
        if(!pw) return false;
        fillFromPasswd(pw,out);
        return true;
    }
        return false;
    }

    // /etc/shadow's 2nd field. Not found (no shadow entry at all) is
    // /etc/shadow.yaml's passwd field. Not found (no shadow entry at all) is
    // reported separately from "found but empty" (passwordless account) --
    // callers that care about the distinction check the return value.
    inline bool lookupShadowHash(const std::string &username,std::string &hashOut){
        std::ifstream in("/etc/shadow");
        std::string line;
        while(std::getline(in,line)){
            std::vector<std::string> f=splitColon(line);
            if(f.size()<2 || f[0]!=username) continue;
            hashOut=f[1];
        struct spwd *sp=getspnam(username.c_str());
        if(!sp) return false;
        hashOut=sp->sp_pwdp;
        return true;
    }
        return false;
    }

    // Empty hash: passwordless account (classic Unix semantics -- login
    // allowed with no password prompt at all). "!"/"!!"/"*"-prefixed:
@@ -166,44 +153,14 @@ namespace fresh { namespace auth {
        return pass;
    }

    // Rewrites /etc/shadow's hash field for one user, via a temp file +
    // rename (atomic, never leaves a half-written shadow file behind on a
    // crash/power loss mid-write). Caller must already have permission to
    // write /etc/shadow (this system has no setuid bit machinery -- passwd
    // is expected to run as root already, e.g. from an install/provisioning
    // step or a root shell, not as an unprivileged self-service tool).
    // Real, atomic (temp file + rename, done inside shadow_update_hash()
    // itself) update of one user's /etc/shadow.yaml passwd field. Caller
    // must already have permission to write /etc/shadow.yaml (this system
    // has no setuid bit machinery -- passwd is expected to run as root
    // already, e.g. from an install/provisioning step or a root shell, not
    // as an unprivileged self-service tool).
    inline bool rewriteShadowHash(const std::string &username,const std::string &newHash){
        std::ifstream in("/etc/shadow");
        if(!in.is_open()) return false;

        std::string out;
        std::string line;
        bool found=false;
        while(std::getline(in,line)){
            std::vector<std::string> f=splitColon(line);
            if(!f.empty() && f[0]==username && f.size()>=2){
                f[1]=newHash;
                std::string rebuilt;
                for(size_t i=0;i<f.size();++i){
                    if(i) rebuilt+=":";
                    rebuilt+=f[i];
                }
                out+=rebuilt+"\n";
                found=true;
            }else{
                out+=line+"\n";
            }
        }
        in.close();
        if(!found) return false;

        std::string tmpPath="/etc/shadow.tmp";
        std::ofstream tmp(tmpPath);
        if(!tmp.is_open()) return false;
        tmp << out;
        tmp.close();

        return std::rename(tmpPath.c_str(),"/etc/shadow")==0;
        return shadow_update_hash(username.c_str(),newHash.c_str())!=0;
    }

}}