blob: 8fce478218b0af89d74028cecde2cf7933c990c3 [file] [log] [blame]
roman41a11e42022-06-22 09:27:08 +02001/**
2 * @file test_pam.c
3 * @author Roman Janota <xjanot04@fit.vutbr.cz>
4 * @brief libnetconf2 Linux PAM keyboard-interactive authentication test
5 *
6 * @copyright
7 * Copyright (c) 2022 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
Michal Vaskoba9f3582023-02-22 10:26:32 +010016#define _GNU_SOURCE
17
roman41a11e42022-06-22 09:27:08 +020018#include <pthread.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include <libyang/libyang.h>
24#include <log.h>
25#include <session_client.h>
26#include <session_server.h>
27
28#include "tests/config.h"
29
30#define nc_assert(cond) if (!(cond)) { fprintf(stderr, "assert failed (%s:%d)\n", __FILE__, __LINE__); abort(); }
31
32#define NC_ACCEPT_TIMEOUT 5000
33#define NC_PS_POLL_TIMEOUT 5000
34
35struct ly_ctx *ctx;
36
37static void *
38server_thread(void *arg)
39{
40 int ret;
41 NC_MSG_TYPE msgtype;
42 struct nc_session *session;
43 struct nc_pollsession *ps;
44
45 (void) arg;
46 ps = nc_ps_new();
47 nc_assert(ps);
48
49 /* accept a session and add it to the poll session structure */
50 msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session);
51 nc_assert(msgtype == NC_MSG_HELLO);
52 ret = nc_ps_add_session(ps, session);
53 nc_assert(!ret);
54 ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
55 nc_assert(ret & NC_PSPOLL_RPC);
Michal Vasko78939072022-12-12 07:43:18 +010056 ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
57 nc_assert(ret & NC_PSPOLL_RPC);
58 nc_ps_clear(ps, 1, NULL);
roman41a11e42022-06-22 09:27:08 +020059
60 nc_ps_free(ps);
61 nc_thread_destroy();
62 return NULL;
63}
64
65static int
66clb_hostkeys(const char *name, void *user_data, char **privkey_path, char **privkey_data,
67 NC_SSH_KEY_TYPE *privkey_type)
68{
69 (void) user_data;
70 (void) privkey_data;
71 (void) privkey_type;
72
73 /* set the path to the testing private keys */
74 if (!strcmp(name, "key_rsa")) {
75 *privkey_path = strdup(TESTS_DIR "/data/key_rsa");
76 return 0;
77 } else if (!strcmp(name, "key_dsa")) {
78 *privkey_path = strdup(TESTS_DIR "/data/key_dsa");
79 return 0;
80 }
81
82 return 1;
83}
84
85static char *
86auth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv)
87{
88 (void) instruction;
89 (void) echo;
90 (void) auth_name;
91 (void) priv;
92
93 /* send the replies to keyboard-interactive authentication */
94 if (strstr(prompt, "backwards")) {
95 return strdup("tset");
96 } else if (strstr(prompt, "1+1")) {
97 return strdup("2");
98 } else {
99 return NULL;
100 }
101}
102
103static int
104ssh_hostkey_check_clb(const char *hostname, ssh_session session, void *priv)
105{
106 (void)hostname;
107 (void)session;
108 (void)priv;
109 /* redundant in this test, nonetheless this callback has to be set */
110
111 return 0;
112}
113
114static void *
115client_thread(void *arg)
116{
117 (void) arg;
118 int ret;
119 struct nc_session *session = NULL;
120
121 printf("SSH client started.\n");
122
123 /* initialize client */
124 nc_client_init();
125 ret = nc_client_set_schema_searchpath(TESTS_DIR "/data/modules");
126 nc_assert(!ret);
127 /* skip the knownhost check */
128 nc_client_ssh_set_auth_hostkey_check_clb(ssh_hostkey_check_clb, NULL);
129
130 ret = nc_client_ssh_set_username("test");
131 nc_assert(!ret);
132
133 /* set keyboard-interactive authentication callback */
134 nc_client_ssh_set_auth_interactive_clb(auth_interactive, NULL);
Jan Kundráte56611b2022-07-26 11:26:12 +0200135 session = nc_connect_ssh("0.0.0.0", 6002, NULL);
roman41a11e42022-06-22 09:27:08 +0200136 nc_assert(session);
137
138 printf("SSH client finished.\n");
139 nc_client_destroy();
140
141 nc_session_free(session, NULL);
142 nc_thread_destroy();
143 return NULL;
144}
145
146int
147main(void)
148{
149 int ret, i;
150 pthread_t tids[2];
151
152 ly_ctx_new(TESTS_DIR "/data/modules", 0, &ctx);
153 nc_assert(ctx);
154 ly_ctx_load_module(ctx, "ietf-netconf", NULL, NULL);
155
156 nc_verbosity(NC_VERB_VERBOSE);
157 nc_server_init();
158
159 /* set callback */
160 nc_server_ssh_set_hostkey_clb(clb_hostkeys, NULL, NULL);
161
162 /* do first, so that client can connect on SSH */
163 ret = nc_server_add_endpt("main_ssh", NC_TI_LIBSSH);
164 nc_assert(!ret);
165 ret = nc_server_endpt_set_address("main_ssh", "0.0.0.0");
166 nc_assert(!ret);
Jan Kundráte56611b2022-07-26 11:26:12 +0200167 ret = nc_server_endpt_set_port("main_ssh", 6002);
roman41a11e42022-06-22 09:27:08 +0200168 nc_assert(!ret);
169 ret = nc_server_ssh_endpt_add_hostkey("main_ssh", "key_rsa", -1);
170 nc_assert(!ret);
171
172 /* in order to use the Linux PAM keyboard-interactive method,
173 * the PAM module has to know where to find the desired configuration file */
romane15ad542022-07-27 10:03:46 +0200174 ret = nc_server_ssh_set_pam_conf_path("netconf.conf", BUILD_DIR "/tests");
roman41a11e42022-06-22 09:27:08 +0200175 nc_assert(!ret);
176
177 /* only want to test keyboard-interactive auth method */
178 ret = nc_server_ssh_endpt_set_auth_methods("main_ssh", NC_SSH_AUTH_INTERACTIVE);
179 nc_assert(!ret);
180
181 ret = pthread_create(&tids[0], NULL, client_thread, NULL);
182 nc_assert(!ret);
183 ret = pthread_create(&tids[1], NULL, server_thread, NULL);
184 nc_assert(!ret);
185
186 for (i = 0; i < 2; i++) {
187 pthread_join(tids[i], NULL);
188 }
189
190 nc_server_destroy();
191 ly_ctx_destroy(ctx);
192 return 0;
193}