1 /*
2  * Copyright (c) 2015 Yubico AB
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *
12  *     * Redistributions in binary form must reproduce the above
13  *       copyright notice, this list of conditions and the following
14  *       disclaimer in the documentation and/or other materials provided
15  *       with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <stdio.h>
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include <sys/types.h>
36 #include <signal.h>
37 #include <unistd.h>
38 
39 #include <assert.h>
40 
41 #include <security/pam_appl.h>
42 #ifdef HAVE_PAM_MODUTIL_DROP_PRIV
43 #include <security/pam_modutil.h>
44 #else
45 #include <pwd.h>
46 struct pam_modutil_privs {
47   int noop;
48 };
49 #endif
50 
51 #define YKVAL_PORT1 "17502"
52 #define YKVAL_PORT2 "30559"
53 #define LDAP_PORT "52825"
54 
55 #define YKVAL SRCDIR"/aux/ykval.pl"
56 #define LDAP SRCDIR"/aux/ldap.pl"
57 #define AUTHFILE SRCDIR"/aux/authfile"
58 
59 static struct data {
60   const char user[255];
61   const char otp[255];
62 } _data[] = {
63   {"foo", "vvincredibletrerdegkkrkkneieultcjdghrejjbckh"},
64   {"bar", "vvincredibletrerdegkkrkkneieultcjdghrejjbckh"},
65   {"foo", "vvincrediblltrerdegkkrkkneieultcjdghrejjbckh"},
66   {"foo", "vvincredibletrerdegkkrkkneieultcjdghrejjbckl"},
67   {"test", "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"},
68   {"foo", ""},
69   {"bar", ""},
70   {"nokeys", ""},
71   {"foo", "testpasswordvvincredibletrerdegkkrkkneieultcjdghrejjbckh"},
72   {"foo", "testpassword"},
73   {"bar", "testpassword"},
74 };
75 
76 
77 static const char *ldap_cfg[] = {
78   "id=1",
79   "urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify;http://localhost:"YKVAL_PORT1"/wsapi/2/verify",
80   "ldap_uri=ldap://localhost:"LDAP_PORT,
81   "ldapdn=ou=users,dc=example,dc=com",
82   "user_attr=uid",
83   "yubi_attr=yubiKeyId",
84   "debug"
85 };
86 
87 static const char *ldap_cfg2[] = {
88   "id=1",
89   "urllist=http://localhost:"YKVAL_PORT1"/wsapi/2/verify;http://localhost:"YKVAL_PORT2"/wsapi/2/verify",
90   "ldap_uri=ldap://localhost:"LDAP_PORT,
91   "ldap_filter=(uid=%u)",
92   "yubi_attr=yubiKeyId",
93   "debug"
94 };
95 
test_get_data(void * id)96 static const struct data *test_get_data(void *id) {
97   return &_data[(long)id];
98 }
99 
100 #ifdef OPENPAM
pam_strerror(const pam_handle_t * pamh,int errnum)101 const char * pam_strerror(const pam_handle_t *pamh, int errnum) {
102 #else
103 const char * pam_strerror(pam_handle_t *pamh, int errnum) {
104 #endif
105   fprintf(stderr, "in pam_strerror()\n");
106   return "error";
107 }
108 
109 int pam_set_data(pam_handle_t *pamh, const char *module_data_name, void *data,
110     void (*cleanup)(pam_handle_t *pamh, void *data, int error_status)) {
111   fprintf(stderr, "in pam_set_data() %s\n", module_data_name);
112   return PAM_SUCCESS;
113 }
114 
115 int pam_get_data(const pam_handle_t *pamh, const char *module_data_name, const void **data) {
116   fprintf(stderr, "in pam_get_data() %s\n", module_data_name);
117   return PAM_SUCCESS;
118 }
119 
120 #ifdef OPENPAM
121 int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt) {
122 #else
123 int pam_get_user(const pam_handle_t *pamh, const char **user, const char *prompt) {
124 #endif
125   fprintf(stderr, "in pam_get_user()\n");
126   *user = test_get_data((void*)pamh)->user;
127   return PAM_SUCCESS;
128 }
129 
130 static int conv_func(int num_msg, const struct pam_message **msg,
131     struct pam_response **resp, void *appdata_ptr) {
132   struct pam_response *reply;
133   fprintf(stderr, "in conv_func()\n");
134   if(num_msg != 1) {
135     return PAM_CONV_ERR;
136   }
137 
138   reply = malloc(sizeof(struct pam_response));
139   reply->resp = strdup(test_get_data(appdata_ptr)->otp);
140   *resp = reply;
141   return PAM_SUCCESS;
142 }
143 
144 static struct pam_conv pam_conversation = {
145   conv_func,
146   NULL,
147 };
148 
149 int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) {
150   fprintf(stderr, "in pam_get_item() %d for %d\n", item_type, (int)(uintptr_t)pamh);
151   if(item_type == PAM_CONV) {
152     pam_conversation.appdata_ptr = (void*)pamh;
153     *item = &pam_conversation;
154   }
155   if(item_type == PAM_AUTHTOK && pamh >= (pam_handle_t*)8) {
156     *item = (void*)_data[(int)(uintptr_t)pamh].otp;
157   }
158   return PAM_SUCCESS;
159 }
160 
161 int pam_modutil_drop_priv(pam_handle_t *pamh, struct pam_modutil_privs *p,
162     const struct passwd *pw) {
163   fprintf(stderr, "in pam_modutil_drop_priv()\n");
164   return PAM_SUCCESS;
165 }
166 
167 int pam_modutil_regain_priv(pam_handle_t *pamh, struct pam_modutil_privs *p) {
168   fprintf(stderr, "in pam_modutil_regain_priv()\n");
169   return PAM_SUCCESS;
170 }
171 
172 int pam_set_item(pam_handle_t *pamh, int item_type, const void *item) {
173   fprintf(stderr, "in pam_set_item()\n");
174   return PAM_SUCCESS;
175 }
176 
177 static int test_authenticate1(void) {
178   const char *cfg[] = {
179     "id=1",
180     "url=http://localhost:"YKVAL_PORT1"/wsapi/2/verify?id=%d&otp=%s",
181     "authfile="AUTHFILE,
182     "debug",
183   };
184   return pam_sm_authenticate(0, 0, sizeof(cfg) / sizeof(char*), cfg);
185 }
186 
187 static int test_authenticate2(void) {
188   const char *cfg[] = {
189     "id=1",
190     "urllist=http://localhost:"YKVAL_PORT1"/wsapi/2/verify;http://localhost:"YKVAL_PORT2"/wsapi/2/verify",
191     "authfile="AUTHFILE,
192     "debug",
193   };
194   return pam_sm_authenticate(0, 0, sizeof(cfg) / sizeof(char*), cfg);
195 }
196 
197 static int test_authenticate3(void) {
198   const char *cfg[] = {
199     "id=1",
200     "urllist=http://localhost:"YKVAL_PORT1"/wsapi/2/verify",
201     "authfile="AUTHFILE,
202     "debug",
203   };
204   return pam_sm_authenticate(4, 0, sizeof(cfg) / sizeof(char*), cfg);
205 }
206 
207 static int test_authenticate4(void) {
208   const char *cfg[] = {
209     "id=1",
210     "urllist=http://localhost:"YKVAL_PORT1"/wsapi/2/verify;http://localhost:"YKVAL_PORT2"/wsapi/2/verify",
211     "authfile="AUTHFILE,
212     "debug",
213   };
214   return pam_sm_authenticate(5, 0, sizeof(cfg) / sizeof(char*), cfg);
215 }
216 
217 static int test_authenticate5(void) {
218   const char *cfg[] = {
219     "id=1",
220     "urllist=http://localhost:"YKVAL_PORT1"/wsapi/2/verify;http://localhost:"YKVAL_PORT2"/wsapi/2/verify",
221     "authfile="AUTHFILE,
222     "debug",
223   };
224   return pam_sm_authenticate(6, 0, sizeof(cfg) / sizeof(char*), cfg);
225 }
226 
227 static int test_fail_authenticate1(void) {
228   const char *cfg[] = {
229     "id=1",
230     "urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify;http://localhost:"YKVAL_PORT1"/wsapi/2/verify",
231     "authfile="AUTHFILE,
232     "debug"
233   };
234   return pam_sm_authenticate(1, 0, sizeof(cfg) / sizeof(char*), cfg);
235 }
236 
237 static int test_fail_authenticate2(void) {
238   const char *cfg[] = {
239     "id=1",
240     "urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify;http://localhost:"YKVAL_PORT1"/wsapi/2/verify",
241     "authfile="AUTHFILE,
242     "debug"
243   };
244   return pam_sm_authenticate(2, 0, sizeof(cfg) / sizeof(char*), cfg);
245 }
246 
247 static int test_fail_authenticate3(void) {
248   const char *cfg[] = {
249     "id=1",
250     "urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify",
251     "authfile="AUTHFILE,
252     "debug"
253   };
254   return pam_sm_authenticate(3, 0, sizeof(cfg) / sizeof(char*), cfg);
255 }
256 
257 static int test_firstpass_authenticate(void) {
258   const char *cfg[] = {
259     "id=1",
260     "urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify;http://localhost:"YKVAL_PORT1"/wsapi/2/verify",
261     "authfile="AUTHFILE,
262     "use_first_pass",
263     "debug"
264   };
265   return pam_sm_authenticate(8, 0, sizeof(cfg) / sizeof(char*), cfg);
266 }
267 
268 static int test_firstpass_fail(void) {
269   const char *cfg[] = {
270     "id=1",
271     "urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify;http://localhost:"YKVAL_PORT1"/wsapi/2/verify",
272     "authfile="AUTHFILE,
273     "use_first_pass",
274     "debug"
275   };
276   return pam_sm_authenticate(9, 0, sizeof(cfg) / sizeof(char*), cfg);
277 }
278 
279 static int test_firstpass_fail2(void) {
280   const char *cfg[] = {
281     "id=1",
282     "urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify;http://localhost:"YKVAL_PORT1"/wsapi/2/verify",
283     "authfile="AUTHFILE,
284     "use_first_pass",
285     "debug"
286   };
287   return pam_sm_authenticate(10, 0, sizeof(cfg) / sizeof(char*), cfg);
288 }
289 
290 static int test_authenticate_ldap1(void) {
291   return pam_sm_authenticate(0, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
292 }
293 
294 static int test_authenticate_ldap_fail1(void) {
295   return pam_sm_authenticate(1, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
296 }
297 
298 static int test_authenticate_ldap_fail2(void) {
299   return pam_sm_authenticate(2, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
300 }
301 
302 static int test_authenticate_ldap2(void) {
303   return pam_sm_authenticate(4, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
304 }
305 
306 static int test_authenticate_ldap3(void) {
307   return pam_sm_authenticate(4, 0, sizeof(ldap_cfg2) / sizeof(char*), ldap_cfg2);
308 }
309 
310 static int test_authenticate_ldap4(void) {
311   return pam_sm_authenticate(5, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
312 }
313 
314 static int test_authenticate_ldap5(void) {
315   return pam_sm_authenticate(6, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
316 }
317 
318 static int test_authenticate_ldap6(void) {
319   return pam_sm_authenticate(7, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
320 }
321 
322 static pid_t run_mock(const char *port, const char *type) {
323   pid_t pid = fork();
324   if(pid == 0) {
325     execlp(type, type, port, NULL);
326   }
327   return pid;
328 }
329 
330 int main(void) {
331   int ret = 0;
332   pid_t child = run_mock(YKVAL_PORT1, YKVAL);
333   pid_t child2 = run_mock(YKVAL_PORT2, YKVAL);
334 #ifdef HAVE_LIBLDAP
335   pid_t child3 = run_mock(LDAP_PORT, LDAP);
336 #endif
337 
338   /* Give the "server" time to settle */
339   sleep(1);
340 
341   if(test_authenticate1() != PAM_SUCCESS) {
342     ret = 1;
343     goto out;
344   }
345   if(test_authenticate2() != PAM_SUCCESS) {
346     ret = 2;
347     goto out;
348   }
349   if(test_fail_authenticate1() != PAM_USER_UNKNOWN) {
350     ret = 3;
351     goto out;
352   }
353   if(test_fail_authenticate2() != PAM_AUTH_ERR) {
354     ret = 4;
355     goto out;
356   }
357   if(test_fail_authenticate3() != PAM_AUTH_ERR) {
358     ret = 5;
359     goto out;
360   }
361   if(test_authenticate3() != PAM_SUCCESS) {
362     ret = 6;
363     goto out;
364   }
365   if(test_authenticate4() != PAM_AUTH_ERR) {
366     ret = 7;
367     goto out;
368   }
369   if(test_authenticate5() != PAM_USER_UNKNOWN) {
370     ret = 8;
371     goto out;
372   }
373   if(test_firstpass_authenticate() != PAM_SUCCESS) {
374     ret = 9;
375     goto out;
376   }
377   if(test_firstpass_fail() != PAM_AUTH_ERR) {
378     ret = 10;
379     goto out;
380   }
381   if(test_firstpass_fail2() != PAM_USER_UNKNOWN) {
382     ret = 11;
383     goto out;
384   }
385 #ifdef HAVE_LIBLDAP
386   if(test_authenticate_ldap1() != PAM_SUCCESS) {
387     ret = 1001;
388     goto out;
389   }
390   if(test_authenticate_ldap_fail1() != PAM_USER_UNKNOWN) {
391     ret = 1002;
392     goto out;
393   }
394   if(test_authenticate_ldap_fail2() != PAM_AUTH_ERR) {
395     ret = 1003;
396     goto out;
397   }
398   if(test_authenticate_ldap2() != PAM_SUCCESS) {
399     ret = 1004;
400     goto out;
401   }
402   if(test_authenticate_ldap3() != PAM_SUCCESS) {
403     ret = 1005;
404     goto out;
405   }
406   if(test_authenticate_ldap4() != PAM_AUTH_ERR) {
407     ret = 1006;
408     goto out;
409   }
410   if(test_authenticate_ldap5() != PAM_USER_UNKNOWN) {
411     ret = 1007;
412     goto out;
413   }
414   if(test_authenticate_ldap6() != PAM_USER_UNKNOWN) {
415     ret = 1008;
416     goto out;
417   }
418 #endif
419 
420 out:
421   kill(child, 9);
422   kill(child2, 9);
423 #ifdef HAVE_LIBLDAP
424   kill(child3, 9);
425   printf("killed %d, %d and %d\n", child, child2, child3);
426 #else
427   printf("killed %d and %d\n", child, child2);
428 #endif
429   if(ret != 0) {
430     fprintf(stderr, "test %d failed!\n", ret);
431   }
432   return ret;
433 }
434