summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/crypto/ssh/test/sshd_test_pw.c
blob: 2794a563a41726e8b4fc97b80dceb2075d70b88e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// 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 <string.h>
#include <pwd.h>
#include <shadow.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

#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;
}