Re: Homeverzeichnis in /etc/passwd als relativer Pfad

Autor: Raphael Eiselstein <rabe_at_uugrn.org>
Datum: Sat, 27 Oct 2012 03:01:33 +0200
Hallo Markus,

On Thu, Oct 25, 2012 at 11:09:42PM -0200, Markus Demleitner wrote:
> > Eine Option wäre, den Zugriff auf die Datei /etc/passwd zu maskieren,
> > d.h. ein fopen("/etc/passwd") transparent für den jeweiligen Prozess
> > tatsächlich umzuleiten auf $DIR/conf/etc/passwd, also eine Art
> > Mapping-Datei / Alias-Datei, die von zB glibc oder aber im Kernel
> > Anwendung findet. 
> Uh.  Du bist sicher, dass du nicht lieber einen winzigen Patch in die
> ssh-Quellen reinfummeln willst?  

Nein, will ich nicht. Ich will das Verhalten ja nur für einen bestimmten
Benutzer verändern.

> Ich gebe zu, an ssh rumfummeln ist
> immer etwas aufregend, aber eigentlich sieht der Kram recht harmlos
> aus (ssh.c, einfach nach _PATH_SSH_USER_CONFFILE suchen; in 5.5p1 ist
> das in der Gegend von Zeile 650).  Aber ein Check auf ein Configfile
> macht dann eigentlich nicht viel was anderes als der Wrapper oben,
> der auch noch gleich den Vorteil hat, dass man ihn statt ssh dann
> lieber ssh-spezial nennen kann, so dass die Leute sehen, dass da
> Magie passiert.

Ich habe mir heute mal ne Stunde Zeit genommen und in den Sourcen von
openssh-portable rumgesucht. Ich habe in glob.c Code gefunden, der 
eigentlich  das tun sollte, was ich erwarten würde: ~ resolven nach
getenv("HOME").

---------------------------------------------------------------------------
static const Char *
globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t
*pglob)
{
...
#if 0
                if (issetugid() != 0 || (h = getenv("HOME")) == NULL) {
#endif
                if ((getuid() != geteuid()) || (h = getenv("HOME")) == NULL) {
                        if ((pwd = getpwuid(getuid())) == NULL)
                                return pattern;
                        else
                                h = pwd->pw_dir;
...
}
---------------------------------------------------------------------------

 
In getpwuid(3) steht entsprechend auch, dass man das so tun *sollte*

       The  pw_dir field contains the name of the ini‐
       tial working directory of the user.  Login pro‐
       grams use the value of this field to initialize
       the HOME environment  variable  for  the  login
       shell.   An application that wants to determine
       its user's home directory  should  inspect  the
       value  of  HOME  (rather  than  the value getp‐
       wuid(getuid())->pw_dir) since this  allows  the
       user to modify their notion of "the home direc‐
       tory" during a login session.  To determine the
       (initial) home directory of another user, it is
       necessary to  use  getpwnam("username")->pw_dir
       or similar.

Es scheint, dass ssh(1) die diversen(!) Konstanten in der Form
"~/.ssh/..." nicht über HOME evaluiert sondern über offensichtlich 
über getpwuid(getuid())->pw_dir).

Obiger Code ist aus /openbsd-compat/glob.c, die Funktionen hängen so
zusammen: glob() --> glob0() --> globtilde()

Also habe ich den Code nach "glob(" gegreppt:

/work/usr/ports/security/openssh-portable/work/openssh-5.8p2]# find .  -type f -name "*.c"  -exec grep -l "glob(" {} +
./openbsd-compat/glob.c
./sftp.c
./sftp-glob.c

Der globbing-Code wird scheinbar nur für sftp verwendet, scheinbar 
nicht für ssh(1). 

Zugriff auf "pw_dir" (das ist der Name des Feldes aus /etc/passwd, in
dem das Homeverzeichnis steht) wird an sehr vielen Stellen verwendet:

find . -type f -name "*.c"  -exec grep -l "pw_dir" {} +
./monitor.c
./auth.c
./ssh-rand-helper.c
./ssh-add.c
./ssh-keygen.c
./auth-rhosts.c
./misc.c
./openbsd-compat/glob.c
./session.c
./monitor_wrap.c
./ssh.c

Zum Beispiel hier in ssh.c:

main(...)
{
... 
       /* Get user data. */
        pw = getpwuid(original_real_uid);
        if (!pw) {
                logit("You don't exist, go away!");
                exit(255);
        }
        /* Take a copy of the returned structure. */
        pw = pwcopy(pw);
...
        /*
         * Read per-user configuration file.  Ignore the system wide
         * config
         * file if the user specifies a config file on the command line.
         */
        if (config != NULL) {
                if (!read_config_file(config, host, &options, 0))
                        fatal("Can't open user config file %.100s: "
                            "%.100s", config, strerror(errno));
        } else {
                r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir,
                    _PATH_SSH_USER_CONFFILE);
                if (r > 0 && (size_t)r < sizeof(buf))
                        (void)read_config_file(buf, host, &options, 1);
...

}

Oder kurz:
* pw = getpwuid(...)
* pw->pw_dir 

Hier wird also in ssh.c gezielt nur die Information benutzt, die aus
getpwuid() kommt (also das Homeverzeichnis anhand von /etc/passwd)

Ich kann sowas für sshd(8) nachvollziehen, weil da macht es Sinn *NICHT*
$HOME zu verwenden oder wenn der Benutzer root-Rechte hat.

Aber wenn ein normaler Benutzer ssh(1) verwendet, dann sollte ihm
gestattet sein, über Anderung von HOME=/some/where/ auf andere Dateien
zu verweisen, die er ohnehin lokal verwenden oder überschreiben könnte.

Ich verstehe also den Sicherheitsgewinn nicht, wieso nicht HOME
verwendet wird.

Fazit: SSH ist ein ziemliches Biest!


[mapping für fopen()]
> LD_PRELOAD; zumindest auf glibc-Systemen müsstest du in ld.so(8) was
> finden.  Das Zeug ist für alles mögliche gut (ich erinnere mich

Nachdem ich etwas vom Code gelesen und oberflächlich verstanden habe,
würde ich nur für den Aufgruf von ssh(1) die Funktion getpwuid()
überladen, sodass getpwuid() mir etwas liefert, was ich über eine
Umgebungsvariable steuern kann, zB HOME_OVERRIDE=/opt/foo/bla/conf/
würde (falls gesetzt) den Wert ersetzen, den getpwuid() sonst aus
/etc/passwd ziehen würde, sodass ssh(1) dann nicht HOME sondern effektiv
HOME_OVERRIDE bekommt.

Das ist sicher die Hardcore-Variante, wäre aber gegenüber dem restlichen
System und Standardverhalten minimalinvasiv anwendbar, da es nur on-demand
per LD_PRELOAD verwendet wird.

Vermutlich könnte man sich den erforderlichen Code per copy-paste aus
den openssh-Quellen zusammenbauen. Ich hab schlichtweg aber keine Ahnung
wie man C entwickelt sodass es das oben beschriebene (HOME_OVERRIDE
nehmen falls gesetzt) tut.

Hätte den Vorteil, dass man das für alles verwenden könnte, was ähnlich
garstig ist wie openssh.

Aktuell lebe ich mit dem Hack, dass ich in /etc/passwd als
Homeverzeichnis "./conf" eingetragen habe, was dann abhängig vom
aktuellen Pfad verwendet wird.

Gruß
Raphael

-- 
Raphael Eiselstein <rabe@uugrn.org>               http://rabe.uugrn.org/
xmpp:freibyter@gmx.de  | https://www.xing.com/profile/Raphael_Eiselstein   
GnuPG:                E7B2 1D66 3AF2 EDC7 9828  6D7A 9CDA 3E7B 10CA 9F2D
.........|.........|.........|.........|.........|.........|.........|..


-- 
UUGRN e.V. http://www.uugrn.org/
http://mailman.uugrn.org/mailman/listinfo/uugrn
Wiki: https://wiki.uugrn.org/UUGRN:Mailingliste
Archiv: http://lists.uugrn.org/

Empfangen am 27.10.2012

Dieses Archiv wurde generiert von hypermail 2.2.0 : 27.10.2012 CEST