git.fiddlerwoaroof.com
Browse code

conversation uses pam correctly

Greg Wiley authored on 20/04/2017 18:13:29
Showing 6 changed files
... ...
@@ -35,15 +35,15 @@ request_test: request_test.o request.o
35 35
 validator_test: validator_test.o validator.o
36 36
 	$(CXX) $(CXXFLAGS) $(CPPFLAGS)  -o $@ $^
37 37
 
38
-# conversation_test: conversation_test.o conversation.o
39
-# 	$(CXX) $(CXXFLAGS) $(CPPFLAGS)  -o $@ $^
38
+conversation_test: conversation_test.o conversation.o
39
+	$(CXX) $(CXXFLAGS) $(CPPFLAGS)  -o $@ $^
40 40
 
41 41
 .PHONY: test
42
-test: validator_test request_test dual_control_test
42
+test: validator_test request_test dual_control_test conversation_test
43 43
 	@./validator_test
44 44
 	@./request_test
45 45
 	@./dual_control_test
46
-#	@./conversation_test
46
+	@./conversation_test
47 47
 
48 48
 .PHONY: install
49 49
 install: $(OBJS)
... ...
@@ -5,6 +5,25 @@
5 5
 
6 6
 #include "conversation.h"
7 7
 
8
+
9
+
10
+
11
+
12
+namespace
13
+{
14
+    class impl : public conversation_ifc
15
+    {
16
+        public:
17
+           conversation_result initiate (const pam_request &request) {
18
+               return {"user","token"};
19
+           }
20
+    };
21
+}
22
+
23
+conversation create_conversation(pam &pam) {
24
+    return conversation(std::shared_ptr<conversation_ifc>(new impl));
25
+}
26
+
8 27
 /*
9 28
 pam_token_conversation::pam_token_conversation (pam_handle_t *pamh,
10 29
         const pam_p pam)
... ...
@@ -5,6 +5,7 @@
5 5
 #include <memory>
6 6
 
7 7
 #include "request.h"
8
+#include "pam.h"
8 9
 
9 10
 struct conversation_result {
10 11
     std::string user_name;
... ...
@@ -40,5 +41,7 @@ inline conversation wrap (conversation_ifc *delegate)
40 41
     return conversation (std::shared_ptr<conversation_ifc> (delegate));
41 42
 };
42 43
 
44
+conversation create_conversation(pam &pam);
45
+
43 46
 #endif
44 47
 
... ...
@@ -1,344 +1,158 @@
1 1
 #include <vector>
2 2
 #include <algorithm>
3 3
 #include <string>
4
+#include <algorithm>
5
+#include <memory>
6
+#include <cstring>
4 7
 #include <security/pam_modules.h>
5 8
 
9
+#include <iostream>
10
+
11
+#include "request.h"
6 12
 #include "conversation.h"
7 13
 #include "test_util.h"
8 14
 #include "pam.h"
9 15
 
10
-class fake_pam_conversation : public pam_conversation
11
-{
12
-private:
13
-    pam_response response_;
14
-    std::string answer_;
15
-public:
16
-    fake_pam_conversation (const std::string &answer) : answer_ (answer) {}
17
-    int conv (const std::vector<const struct pam_message *> &prompts,
18
-              std::vector<struct pam_response *> &answers)
19
-    {
20
-        if (prompts.size() != 1) {
21
-            throw std::string ("test only supports one prompt");
22
-        }
23
-
24
-        response_.resp_retcode = 0;
25
-        response_.resp = const_cast<char *> (answer_.c_str());
26
-        answers.resize (1);
27
-        answers[0] = &response_;
28
-        return 0;
29
-    }
16
+struct conversation_data {
17
+    std::vector<pam_message> expected_prompts;
18
+    std::vector<pam_response> responses;
19
+    int return_value;
30 20
 };
31 21
 
32
-class fake_failing_conversation: public pam_conversation
22
+bool same_prompts (const std::vector<pam_message> &expected,
23
+                   int num_prompts, const pam_message **actual)
33 24
 {
34
-
35
-public:
36
-    int conv (const std::vector<const struct pam_message *> &prompts,
37
-              std::vector<struct pam_response *> &answers)
38
-    {
39
-        return 1;
25
+    if (expected.size() != num_prompts) {
26
+        return false;
40 27
     }
41
-};
42 28
 
43
-class fake_failing_answer_conversation: public pam_conversation
44
-{
45
-private:
46
-    pam_response response_;
47
-    std::string answer_;
48
-public:
49
-    fake_failing_answer_conversation() : answer_ ("ok:1") {}
50
-    int conv (const std::vector<const struct pam_message *> &prompts,
51
-              std::vector<struct pam_response *> &answers)
52
-    {
53
-        if (prompts.size() != 1) {
54
-            throw std::string ("test only supports one prompt");
29
+    for (int i=0; i< num_prompts; ++i) {
30
+        if (expected[i].msg_style != actual[i]->msg_style) {
31
+            return false;
55 32
         }
56 33
 
57
-        response_.resp_retcode = 13;
58
-        response_.resp = const_cast<char *> (answer_.c_str());
59
-        answers.resize (1);
60
-        answers[0] = &response_;
61
-        return 0;
62
-    }
63
-};
64
-
65
-class match_prompt_text_conversation : public pam_conversation
66
-{
67
-private:
68
-    pam_response response_;
69
-    std::string answer_;
70
-    std::string prompt_;
71
-public:
72
-    match_prompt_text_conversation (const std::string &prompt) : prompt_
73
-        (prompt), answer_ ("ok:123") {}
74
-    int conv (const std::vector<const struct pam_message *> &prompts,
75
-              std::vector<struct pam_response *> &answers)
76
-    {
77
-        if (prompt_ != prompts[0]->msg) {
78
-            throw std::string ("prompt does not match");
34
+        if (std::strcmp (expected[i].msg, actual[i]->msg)) {
35
+            return false;
79 36
         }
80
-
81
-        response_.resp_retcode = 0;
82
-        response_.resp = const_cast<char *> (answer_.c_str());
83
-        answers.resize (1);
84
-        answers[0] = &response_;
85
-        return 0;
86 37
     }
87 38
 
88
-};
39
+    return true;
40
+}
89 41
 
90
-class match_prompt_style_conversation : public pam_conversation
42
+template<class T>
43
+T *address_of (T &t)
91 44
 {
92
-private:
93
-    pam_response response_;
94
-    std::string answer_;
95
-    int style_;
96
-public:
97
-    match_prompt_style_conversation (int style) : style_ (style),
98
-        answer_ ("ok:123") {}
99
-    int conv (const std::vector<const struct pam_message *> &prompts,
100
-              std::vector<struct pam_response *> &answers)
101
-    {
102
-        if (style_ != prompts[0]->msg_style) {
103
-            throw std::string ("style does not match");
104
-        }
45
+    return &t;
46
+}
47
+
48
+int fake_conv (int num_msg, const struct pam_message **msg,
49
+               struct pam_response **resp, void *appdata_ptr)
50
+{
51
+    conversation_data *data = reinterpret_cast<conversation_data *>
52
+                              (appdata_ptr);
105 53
 
106
-        response_.resp_retcode = 0;
107
-        response_.resp = const_cast<char *> (answer_.c_str());
108
-        answers.resize (1);
109
-        answers[0] = &response_;
110
-        return 0;
54
+    if (data->return_value != PAM_SUCCESS) {
55
+        return data->return_value;
111 56
     }
112 57
 
113
-};
58
+    if (!same_prompts (data->expected_prompts, num_msg, msg)) {
59
+        throw std::string ("unexpected prompts");
60
+    }
61
+
62
+    std::vector<pam_response> &responses = data->responses;
63
+    std::transform (responses.begin(), responses.end(), resp,
64
+                    address_of<pam_response>);
65
+    return data->return_value;
66
+}
114 67
 
115
-class fake_pam : public pam
68
+class fake_pam : public pam_ifc
116 69
 {
117 70
 private:
118
-    std::shared_ptr<pam_conversation> conversation_;
71
+    pam_handle *expected_handle_;
72
+    conversation_data conversation_data_;
73
+    pam_conv conv_;
119 74
 public:
120
-    fake_pam (std::shared_ptr<pam_conversation> conversation) : conversation_
121
-        (conversation) {}
122
-    fake_pam() {}
123
-    int get_conversation (pam_handle_t *pamh,
124
-                          std::shared_ptr<pam_conversation> &conversation)
75
+    fake_pam(pam_handle* expected_handle, const conversation_data &conversation_data)
76
+        : expected_handle_(expected_handle),
77
+          conversation_data_(conversation_data)
78
+    {}
79
+    int get_conv (pam_handle *handle, const pam_conv **out)
125 80
     {
126
-        if (conversation_) {
127
-            conversation = conversation_;
128
-            return 0;
81
+        if (expected_handle_ != handle) {
82
+            throw std::string("unexpected handle");
129 83
         }
130
-
131
-        return 12;
84
+        conv_.appdata_ptr = reinterpret_cast<void *>(&conversation_data_);
85
+        conv_.conv = fake_conv;
86
+        *out = &conv_;
87
+        return PAM_SUCCESS;
132 88
     }
133
-};
134
-
135
-int returns_correct_token()
136
-{
137
-    //given
138
-    pam_handle_t *pamh;
139
-    pam_conversation_p fake_conversation = (pam_conversation_p) new
140
-                                           fake_pam_conversation ("user:code");
141
-    pam_p pam = (pam_p)new fake_pam (fake_conversation);
142 89
 
143
-    //when
144
-    pam_token_conversation conversation (pamh, pam);
90
+};
145 91
 
146
-    //then
147
-    check (conversation.token() == "code", "returned incorrect token");
148
-    succeed();
92
+template<class T>
93
+std::shared_ptr<T> share (T *t) {
94
+    return std::shared_ptr<T>(t);
149 95
 }
150 96
 
151
-int returns_correct_user_name()
97
+bool uses_pam_correctly()
152 98
 {
153
-    //given
154
-    pam_handle_t *pamh;
155
-    pam_conversation_p fake_conversation = (pam_conversation_p) new
156
-                                           fake_pam_conversation ("sally:token");
157
-    pam_p pam = (pam_p)new fake_pam (fake_conversation);
158 99
 
159
-    //when
160
-    pam_token_conversation conversation (pamh, pam);
100
+    // given
101
+    pam_handle *handle = reinterpret_cast<pam_handle *> (29039);
102
+    std::string user ("user");
103
+    std::string token ("token");
104
+    pam_message prompt;
105
+    prompt.msg_style = PAM_PROMPT_ECHO_OFF;
106
+    prompt.msg = const_cast<char *>("Dual control token: ");
107
+    pam_response response;
108
+    response.resp_retcode = 0;
109
+    std::string response_text(user + ":" + token);
110
+    response.resp = const_cast<char *>(response_text.c_str());
111
+    conversation_data conversation_data = {
112
+        std::vector<pam_message>(&prompt, &prompt + 1),
113
+        std::vector<pam_response>(&response, &response + 1),
114
+        PAM_SUCCESS
115
+    };
116
+    pam pam (share (new fake_pam (handle, conversation_data)));
117
+    pam_request request (handle, 0, 0, 0);
118
+
119
+    conversation conversation (create_conversation (pam));
120
+
121
+    // when
122
+    conversation_result actual = conversation.initiate (request);
123
+
124
+    // then
125
+    check(actual.user_name == user, "user name does not match");
126
+    check(actual.token == token, "token does not match");
161 127
 
162
-    //then
163
-    check (conversation.user_name() == "sally", "returned incorrect user name");
164 128
     succeed();
165 129
 }
166 130
 
167
-int returns_empty_user_and_token_when_no_colon()
168
-{
169
-    //given
170
-    pam_handle_t *pamh;
171
-    pam_conversation_p fake_conversation = (pam_conversation_p) new
172
-                                           fake_pam_conversation ("sally");
173
-    pam_p pam = (pam_p)new fake_pam (fake_conversation);
174
-
175
-    //when
176
-    pam_token_conversation conversation (pamh, pam);
177
-
178
-    //then
179
-    check (conversation.user_name() == "", "did not return empty user name");
180
-    check (conversation.token() == "", "did not return empty token");
181
-    succeed();
182
-}
131
+RESET_VARS_START
132
+RESET_VARS_END
183 133
 
184
-int returns_empty_user_and_token_when_empty_answer()
134
+int run_tests()
185 135
 {
186
-    //given
187
-    pam_handle_t *pamh;
188
-    pam_conversation_p fake_conversation = (pam_conversation_p) new
189
-                                           fake_pam_conversation ("");
190
-    pam_p pam = (pam_p)new fake_pam (fake_conversation);
191
-
192
-    //when
193
-    pam_token_conversation conversation (pamh, pam);
194
-
195
-    //then
196
-    check (conversation.user_name() == "", "did not return empty user name");
197
-    check (conversation.token() == "", "did not return empty token");
136
+    test (uses_pam_correctly);
198 137
     succeed();
199 138
 }
200 139
 
201
-int returns_empty_token_when_colon_end()
140
+int main (int argc, char **argv)
202 141
 {
203
-    //given
204
-    pam_handle_t *pamh;
205
-    pam_conversation_p fake_conversation = (pam_conversation_p) new
206
-                                           fake_pam_conversation ("sally:");
207
-    pam_p pam = (pam_p)new fake_pam (fake_conversation);
208
-
209
-    //when
210
-    pam_token_conversation conversation (pamh, pam);
211
-
212
-    //then
213
-    check (conversation.user_name() == "sally",
214
-           "did not return empty user name");
215
-    check (conversation.token() == "", "did not return empty token");
216
-    succeed();
142
+    return !run_tests();
217 143
 }
218 144
 
145
+/*
146
+ * int returns_correct_token()
147
+int returns_correct_user_name()
148
+int returns_empty_user_and_token_when_no_colon()
149
+int returns_empty_user_and_token_when_empty_answer()
150
+int returns_empty_token_when_colon_end()
219 151
 int returns_empty_user_when_colon_begin()
220
-{
221
-    //given
222
-    pam_handle_t *pamh;
223
-    pam_conversation_p fake_conversation = (pam_conversation_p) new
224
-                                           fake_pam_conversation (":token");
225
-    pam_p pam = (pam_p)new fake_pam (fake_conversation);
226
-
227
-    //when
228
-    pam_token_conversation conversation (pamh, pam);
229
-
230
-    //then
231
-    check (conversation.user_name() == "", "did not return empty user name");
232
-    check (conversation.token() == "token", "did not return empty token");
233
-    succeed();
234
-}
235
-
236 152
 int returns_empty_user_and_token_when_pam_cant_create_conversation()
237
-{
238
-    // given
239
-    pam_handle_t *pamh;
240
-    pam_p pam = (pam_p)new fake_pam;
241
-
242
-    //when
243
-    pam_token_conversation conversation (pamh, pam);
244
-
245
-    //then
246
-    check (conversation.user_name() == "", "did not return empty user name");
247
-    check (conversation.token() == "", "did not return empty token");
248
-    succeed();
249
-
250
-}
251
-
252 153
 int prompts_user_with_correct_text()
253
-{
254
-    // given
255
-    pam_handle_t *pamh;
256
-    pam_conversation_p match_conversation = (pam_conversation_p) new
257
-                                            match_prompt_text_conversation ("Dual control token: ");
258
-    pam_p pam = (pam_p)new fake_pam (match_conversation);
259
-
260
-    // when / then
261
-    try {
262
-        pam_token_conversation conversation (pamh, pam);
263
-        succeed();
264
-    } catch (const std::string &x) {
265
-        fail();
266
-    }
267
-
268
-}
269
-
270 154
 int prompts_user_with_correct_style()
271
-{
272
-    // given
273
-    pam_handle_t *pamh;
274
-    pam_conversation_p match_conversation = (pam_conversation_p) new
275
-                                            match_prompt_style_conversation (PAM_PROMPT_ECHO_OFF);
276
-    pam_p pam = (pam_p)new fake_pam (match_conversation);
277
-
278
-    // when / then
279
-    try {
280
-        pam_token_conversation conversation (pamh, pam);
281
-        succeed();
282
-    } catch (const std::string &x) {
283
-        fail();
284
-    }
285
-}
286
-
287 155
 int returns_empty_user_and_token_when_conversation_fails()
288
-{
289
-    //given
290
-    pam_handle_t *pamh;
291
-    pam_conversation_p fake_conversation = (pam_conversation_p) new
292
-                                           fake_failing_conversation;
293
-    pam_p pam = (pam_p) new fake_pam (fake_conversation);
294
-
295
-    //when
296
-    pam_token_conversation conversation (pamh, pam);
297
-
298
-    //then
299
-    check (conversation.user_name() == "", "did not return empty user name");
300
-    check (conversation.token() == "", "did not return empty token");
301
-    succeed();
302
-}
303
-
304 156
 int returns_empty_user_and_token_when_conversation_answer_fails()
305
-{
306
-    //given
307
-    pam_handle_t *pamh;
308
-    pam_conversation_p fake_conversation = (pam_conversation_p) new
309
-                                           fake_failing_answer_conversation;
310
-    pam_p pam = (pam_p) new fake_pam (fake_conversation);
311
-
312
-    //when
313
-    pam_token_conversation conversation (pamh, pam);
314
-
315
-    //then
316
-    check (conversation.user_name() == "", "did not return empty user name");
317
-    check (conversation.token() == "", "did not return empty token");
318
-    succeed();
319
-}
320
-
321
-RESET_VARS_START
322
-RESET_VARS_END
323
-
324
-int run_tests()
325
-{
326
-    test (returns_correct_token);
327
-    test (returns_correct_user_name);
328
-    test (returns_empty_user_and_token_when_no_colon);
329
-    test (returns_empty_token_when_colon_end);
330
-    test (returns_empty_user_when_colon_begin);
331
-    test (returns_empty_user_and_token_when_empty_answer);
332
-    test (returns_empty_user_and_token_when_pam_cant_create_conversation);
333
-    test (prompts_user_with_correct_text);
334
-    test (prompts_user_with_correct_style);
335
-    test (returns_empty_user_and_token_when_conversation_fails);
336
-    test (returns_empty_user_and_token_when_conversation_answer_fails);
337
-    succeed();
338
-}
339
-
340
-int main (int argc, char *args[])
341
-{
342
-    return !run_tests();
343
-}
157
+*/
344 158
 
... ...
@@ -1,55 +1,18 @@
1 1
 #include <vector>
2
-#include <tuple>
3 2
 #include <security/pam_modules.h>
4 3
 #include <security/pam_appl.h>
5 4
 
6 5
 #include "pam.h"
7 6
 
8
-std::tuple<int,std::vector<pam_response>> pam_ifc::conv (pam_handle *handle,
9
-                                       const std::vector<pam_message> &prompts)
10
-{
11
-    return std::make_tuple (PAM_SERVICE_ERR, std::vector<pam_response>());
12
-}
13
-
14
-/*
15
-class pam_conversation_impl : public pam_conversation
16
-{
17
-private:
18
-    struct pam_conv *pam_conv_;
19
-public:
20
-    pam_conversation_impl (pam_conv *conv) : pam_conv_ (conv) {}
21
-    int conv (const std::vector<const struct pam_message *> &prompts,
22
-              std::vector<struct pam_response *> &answers);
23
-};
24
-
25
-class pam_impl : public pam
7
+class syspam : public pam_ifc
26 8
 {
27 9
 public:
28
-    int get_conversation (pam_handle_t *pamh,
29
-                          std::shared_ptr<pam_conversation> &conversation);
10
+    int get_conv (pam_handle *handle, const pam_conv **pout);
30 11
 };
31 12
 
32
-int pam_impl::get_conversation (pam_handle_t *pamh,
33
-                                std::shared_ptr<pam_conversation> &conversation)
34
-{
35
-    struct pam_conv *pam_conv;
36
-    int result = pam_get_item (pamh, PAM_CONV, (const void **)&pam_conv);
37
-    conversation.reset (new pam_conversation_impl (pam_conv));
38
-    return result;
39
-}
40
-
41
-int pam_conversation_impl::conv (const
42
-                                 std::vector<const struct pam_message *> &prompts,
43
-                                 std::vector<struct pam_response *> &answers)
44
-{
45
-    return pam_conv_->conv (prompts.size(),
46
-                            const_cast<const struct pam_message **> (prompts.data()),
47
-                            answers.data(), pam_conv_->appdata_ptr);
48
-}
49
-
50
-pam_p get_system_pam()
13
+int syspam::get_conv (pam_handle *handle,
14
+                      std::shared_ptr<pam_conv_ifc> &out)
51 15
 {
52
-    return (pam_p)new pam_impl;
16
+    return pam_get_item (handle, PAM_CONV, (const void **)pout);
53 17
 }
54
-*/
55 18
 
... ...
@@ -1,17 +1,16 @@
1 1
 #ifndef _PAM_H
2 2
 #define _PAM_H
3
-#include <string>
4
-#include <vector>
5 3
 #include <memory>
6
-#include <tuple>
4
+#include <vector>
7 5
 #include <security/pam_modules.h>
8 6
 
9 7
 class pam_ifc
10 8
 {
11 9
 public:
12
-    virtual std::tuple<int,std::vector<pam_response>> conv (pam_handle *handle,
13
-            const std::vector<pam_message> &prompts);
14
-
10
+    virtual int get_conv (pam_handle *handle, const pam_conv **out)
11
+    {
12
+        return PAM_SERVICE_ERR;
13
+    }
15 14
 };
16 15
 
17 16
 class pam : public pam_ifc
... ...
@@ -22,10 +21,9 @@ private:
22 21
 public:
23 22
     pam (const delegate &delegate) : delegate_ (delegate) {}
24 23
     pam() : pam (delegate (new pam_ifc)) {}
25
-    std::tuple<int,std::vector<pam_response>> conv (pam_handle *handle,
26
-                                           const std::vector<pam_message> &prompts)
24
+    int get_conv (pam_handle *handle, const pam_conv **out)
27 25
     {
28
-        return delegate_-> conv (handle, prompts);
26
+        return delegate_->get_conv (handle, out);
29 27
     }
30 28
 };
31 29