1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// sshd_test_pw.c
6// Wrapper to inject test password data for sshd PAM authentication
7//
8// This wrapper implements custom versions of getpwnam, getpwnam_r,
9// getspnam and getspnam_r. These functions first call their real
10// libc versions, then check if the requested user matches test user
11// specified in env variable TEST_USER and if so replace the password
12// with crypted() value of TEST_PASSWD env variable.
13//
14// Compile:
15// gcc -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c
16//
17// Compile with debug:
18// gcc -DVERBOSE -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c
19//
20// Run sshd:
21// LD_PRELOAD="sshd_test_pw.so" TEST_USER="..." TEST_PASSWD="..." sshd ...
22
23//go:build ignore
24
25#define _GNU_SOURCE
26#include <string.h>
27#include <pwd.h>
28#include <shadow.h>
29#include <dlfcn.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <stdio.h>
33
34#ifdef VERBOSE
35#define DEBUG(X...) fprintf(stderr, X)
36#else
37#define DEBUG(X...) while (0) { }
38#endif
39
40/* crypt() password */
41static char *
42pwhash(char *passwd) {
43 return strdup(crypt(passwd, "$6$"));
44}
45
46/* Pointers to real functions in libc */
47static struct passwd * (*real_getpwnam)(const char *) = NULL;
48static int (*real_getpwnam_r)(const char *, struct passwd *, char *, size_t, struct passwd **) = NULL;
49static struct spwd * (*real_getspnam)(const char *) = NULL;
50static int (*real_getspnam_r)(const char *, struct spwd *, char *, size_t, struct spwd **) = NULL;
51
52/* Cached test user and test password */
53static char *test_user = NULL;
54static char *test_passwd_hash = NULL;
55
56static void
57init(void) {
58 /* Fetch real libc function pointers */
59 real_getpwnam = dlsym(RTLD_NEXT, "getpwnam");
60 real_getpwnam_r = dlsym(RTLD_NEXT, "getpwnam_r");
61 real_getspnam = dlsym(RTLD_NEXT, "getspnam");
62 real_getspnam_r = dlsym(RTLD_NEXT, "getspnam_r");
63
64 /* abort if env variables are not defined */
65 if (getenv("TEST_USER") == NULL || getenv("TEST_PASSWD") == NULL) {
66 fprintf(stderr, "env variables TEST_USER and TEST_PASSWD are missing\n");
67 abort();
68 }
69
70 /* Fetch test user and test password from env */
71 test_user = strdup(getenv("TEST_USER"));
72 test_passwd_hash = pwhash(getenv("TEST_PASSWD"));
73
74 DEBUG("sshd_test_pw init():\n");
75 DEBUG("\treal_getpwnam: %p\n", real_getpwnam);
76 DEBUG("\treal_getpwnam_r: %p\n", real_getpwnam_r);
77 DEBUG("\treal_getspnam: %p\n", real_getspnam);
78 DEBUG("\treal_getspnam_r: %p\n", real_getspnam_r);
79 DEBUG("\tTEST_USER: '%s'\n", test_user);
80 DEBUG("\tTEST_PASSWD: '%s'\n", getenv("TEST_PASSWD"));
81 DEBUG("\tTEST_PASSWD_HASH: '%s'\n", test_passwd_hash);
82}
83
84static int
85is_test_user(const char *name) {
86 if (test_user != NULL && strcmp(test_user, name) == 0)
87 return 1;
88 return 0;
89}
90
91/* getpwnam */
92
93struct passwd *
94getpwnam(const char *name) {
95 struct passwd *pw;
96
97 DEBUG("sshd_test_pw getpwnam(%s)\n", name);
98
99 if (real_getpwnam == NULL)
100 init();
101 if ((pw = real_getpwnam(name)) == NULL)
102 return NULL;
103
104 if (is_test_user(name))
105 pw->pw_passwd = strdup(test_passwd_hash);
106
107 return pw;
108}
109
110/* getpwnam_r */
111
112int
113getpwnam_r(const char *name,
114 struct passwd *pwd,
115 char *buf,
116 size_t buflen,
117 struct passwd **result) {
118 int r;
119
120 DEBUG("sshd_test_pw getpwnam_r(%s)\n", name);
121
122 if (real_getpwnam_r == NULL)
123 init();
124 if ((r = real_getpwnam_r(name, pwd, buf, buflen, result)) != 0 || *result == NULL)
125 return r;
126
127 if (is_test_user(name))
128 pwd->pw_passwd = strdup(test_passwd_hash);
129
130 return 0;
131}
132
133/* getspnam */
134
135struct spwd *
136getspnam(const char *name) {
137 struct spwd *sp;
138
139 DEBUG("sshd_test_pw getspnam(%s)\n", name);
140
141 if (real_getspnam == NULL)
142 init();
143 if ((sp = real_getspnam(name)) == NULL)
144 return NULL;
145
146 if (is_test_user(name))
147 sp->sp_pwdp = strdup(test_passwd_hash);
148
149 return sp;
150}
151
152/* getspnam_r */
153
154int
155getspnam_r(const char *name,
156 struct spwd *spbuf,
157 char *buf,
158 size_t buflen,
159 struct spwd **spbufp) {
160 int r;
161
162 DEBUG("sshd_test_pw getspnam_r(%s)\n", name);
163
164 if (real_getspnam_r == NULL)
165 init();
166 if ((r = real_getspnam_r(name, spbuf, buf, buflen, spbufp)) != 0)
167 return r;
168
169 if (is_test_user(name))
170 spbuf->sp_pwdp = strdup(test_passwd_hash);
171
172 return r;
173}
View as plain text