// Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // sshd_test_pw.c // Wrapper to inject test password data for sshd PAM authentication // // This wrapper implements custom versions of getpwnam, getpwnam_r, // getspnam and getspnam_r. These functions first call their real // libc versions, then check if the requested user matches test user // specified in env variable TEST_USER and if so replace the password // with crypted() value of TEST_PASSWD env variable. // // Compile: // gcc -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c // // Compile with debug: // gcc -DVERBOSE -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c // // Run sshd: // LD_PRELOAD="sshd_test_pw.so" TEST_USER="..." TEST_PASSWD="..." sshd ... // +build ignore #define _GNU_SOURCE #include #include #include #include #include #include #include #ifdef VERBOSE #define DEBUG(X...) fprintf(stderr, X) #else #define DEBUG(X...) while (0) { } #endif /* crypt() password */ static char * pwhash(char *passwd) { return strdup(crypt(passwd, "$6$")); } /* Pointers to real functions in libc */ static struct passwd * (*real_getpwnam)(const char *) = NULL; static int (*real_getpwnam_r)(const char *, struct passwd *, char *, size_t, struct passwd **) = NULL; static struct spwd * (*real_getspnam)(const char *) = NULL; static int (*real_getspnam_r)(const char *, struct spwd *, char *, size_t, struct spwd **) = NULL; /* Cached test user and test password */ static char *test_user = NULL; static char *test_passwd_hash = NULL; static void init(void) { /* Fetch real libc function pointers */ real_getpwnam = dlsym(RTLD_NEXT, "getpwnam"); real_getpwnam_r = dlsym(RTLD_NEXT, "getpwnam_r"); real_getspnam = dlsym(RTLD_NEXT, "getspnam"); real_getspnam_r = dlsym(RTLD_NEXT, "getspnam_r"); /* abort if env variables are not defined */ if (getenv("TEST_USER") == NULL || getenv("TEST_PASSWD") == NULL) { fprintf(stderr, "env variables TEST_USER and TEST_PASSWD are missing\n"); abort(); } /* Fetch test user and test password from env */ test_user = strdup(getenv("TEST_USER")); test_passwd_hash = pwhash(getenv("TEST_PASSWD")); DEBUG("sshd_test_pw init():\n"); DEBUG("\treal_getpwnam: %p\n", real_getpwnam); DEBUG("\treal_getpwnam_r: %p\n", real_getpwnam_r); DEBUG("\treal_getspnam: %p\n", real_getspnam); DEBUG("\treal_getspnam_r: %p\n", real_getspnam_r); DEBUG("\tTEST_USER: '%s'\n", test_user); DEBUG("\tTEST_PASSWD: '%s'\n", getenv("TEST_PASSWD")); DEBUG("\tTEST_PASSWD_HASH: '%s'\n", test_passwd_hash); } static int is_test_user(const char *name) { if (test_user != NULL && strcmp(test_user, name) == 0) return 1; return 0; } /* getpwnam */ struct passwd * getpwnam(const char *name) { struct passwd *pw; DEBUG("sshd_test_pw getpwnam(%s)\n", name); if (real_getpwnam == NULL) init(); if ((pw = real_getpwnam(name)) == NULL) return NULL; if (is_test_user(name)) pw->pw_passwd = strdup(test_passwd_hash); return pw; } /* getpwnam_r */ int getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) { int r; DEBUG("sshd_test_pw getpwnam_r(%s)\n", name); if (real_getpwnam_r == NULL) init(); if ((r = real_getpwnam_r(name, pwd, buf, buflen, result)) != 0 || *result == NULL) return r; if (is_test_user(name)) pwd->pw_passwd = strdup(test_passwd_hash); return 0; } /* getspnam */ struct spwd * getspnam(const char *name) { struct spwd *sp; DEBUG("sshd_test_pw getspnam(%s)\n", name); if (real_getspnam == NULL) init(); if ((sp = real_getspnam(name)) == NULL) return NULL; if (is_test_user(name)) sp->sp_pwdp = strdup(test_passwd_hash); return sp; } /* getspnam_r */ int getspnam_r(const char *name, struct spwd *spbuf, char *buf, size_t buflen, struct spwd **spbufp) { int r; DEBUG("sshd_test_pw getspnam_r(%s)\n", name); if (real_getspnam_r == NULL) init(); if ((r = real_getspnam_r(name, spbuf, buf, buflen, spbufp)) != 0) return r; if (is_test_user(name)) spbuf->sp_pwdp = strdup(test_passwd_hash); return r; }