diff options
Diffstat (limited to 'ident/pam.c')
-rw-r--r-- | ident/pam.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/ident/pam.c b/ident/pam.c new file mode 100644 index 0000000..ef32c4d --- /dev/null +++ b/ident/pam.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* This file is part of GNU Pies. | ||
2 | Copyright (C) 2015 Sergey Poznyakoff | ||
3 | |||
4 | GNU Pies is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation; either version 3, or (at your option) | ||
7 | any later version. | ||
8 | |||
9 | GNU Pies is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */ | ||
16 | |||
17 | #include "ident.h" | ||
18 | #include <grp.h> | ||
19 | #include <security/pam_appl.h> | ||
20 | |||
21 | struct pam_identity_provider_data | ||
22 | { | ||
23 | char *service; | ||
24 | }; | ||
25 | |||
26 | struct pam_cred | ||
27 | { | ||
28 | const char *user; | ||
29 | const char *pass; | ||
30 | }; | ||
31 | |||
32 | #define COPY_STRING(s) (s) ? strdup(s) : NULL | ||
33 | |||
34 | #define overwrite_and_free(ptr) \ | ||
35 | do { \ | ||
36 | char *s = ptr; \ | ||
37 | while (*s) \ | ||
38 | *s++ = 0; \ | ||
39 | } while (0) | ||
40 | |||
41 | #ifndef PAM_AUTHTOK_RECOVER_ERR | ||
42 | # define PAM_AUTHTOK_RECOVER_ERR PAM_CONV_ERR | ||
43 | #endif | ||
44 | |||
45 | static int | ||
46 | pies_conv (int num_msg, const struct pam_message **msg, | ||
47 | struct pam_response **resp, void *appdata_ptr) | ||
48 | { | ||
49 | int status = PAM_SUCCESS; | ||
50 | int i; | ||
51 | struct pam_response *reply = NULL; | ||
52 | struct pam_cred *cred = appdata_ptr; | ||
53 | |||
54 | reply = calloc (num_msg, sizeof (*reply)); | ||
55 | if (!reply) | ||
56 | return PAM_CONV_ERR; | ||
57 | |||
58 | for (i = 0; i < num_msg && status == PAM_SUCCESS; i++) | ||
59 | { | ||
60 | switch (msg[i]->msg_style) | ||
61 | { | ||
62 | case PAM_PROMPT_ECHO_ON: | ||
63 | reply[i].resp_retcode = PAM_SUCCESS; | ||
64 | reply[i].resp = COPY_STRING (cred->user); | ||
65 | /* PAM frees resp */ | ||
66 | break; | ||
67 | |||
68 | case PAM_PROMPT_ECHO_OFF: | ||
69 | if (cred->pass) | ||
70 | { | ||
71 | reply[i].resp_retcode = PAM_SUCCESS; | ||
72 | reply[i].resp = COPY_STRING (cred->pass); | ||
73 | /* PAM frees resp */ | ||
74 | } else | ||
75 | status = PAM_AUTHTOK_RECOVER_ERR; | ||
76 | break; | ||
77 | |||
78 | case PAM_TEXT_INFO: | ||
79 | case PAM_ERROR_MSG: | ||
80 | reply[i].resp_retcode = PAM_SUCCESS; | ||
81 | reply[i].resp = NULL; | ||
82 | break; | ||
83 | |||
84 | default: | ||
85 | status = PAM_CONV_ERR; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | if (status != PAM_SUCCESS) | ||
90 | { | ||
91 | for (i = 0; i < num_msg; i++) | ||
92 | if (reply[i].resp) | ||
93 | { | ||
94 | switch (msg[i]->msg_style) | ||
95 | { | ||
96 | case PAM_PROMPT_ECHO_ON: | ||
97 | case PAM_PROMPT_ECHO_OFF: | ||
98 | overwrite_and_free (reply[i].resp); | ||
99 | break; | ||
100 | |||
101 | case PAM_ERROR_MSG: | ||
102 | case PAM_TEXT_INFO: | ||
103 | free (reply[i].resp); | ||
104 | } | ||
105 | } | ||
106 | free (reply); | ||
107 | } else | ||
108 | *resp = reply; | ||
109 | return status; | ||
110 | } | ||
111 | |||
112 | static int | ||
113 | authenticate (pies_identity_provider_t provider, | ||
114 | pies_identity_t id, char const *passwd) | ||
115 | { | ||
116 | struct pam_identity_provider_data *pamdata = provider->data; | ||
117 | pam_handle_t *pamh; | ||
118 | int pamerror; | ||
119 | struct pam_cred cred = { id->username, passwd }; | ||
120 | struct pam_conv pam_conv = { &pies_conv, &cred }; | ||
121 | char const *service = pamdata->service ? pamdata->service : "pies"; | ||
122 | |||
123 | do | ||
124 | { | ||
125 | pamerror = pam_start (service, id->username, &pam_conv, &pamh); | ||
126 | if (pamerror != PAM_SUCCESS) | ||
127 | break; | ||
128 | |||
129 | pamerror = pam_authenticate (pamh, 0); | ||
130 | if (pamerror != PAM_SUCCESS) | ||
131 | break; | ||
132 | |||
133 | pamerror = pam_acct_mgmt (pamh, 0); | ||
134 | if (pamerror != PAM_SUCCESS) | ||
135 | break; | ||
136 | |||
137 | pamerror = pam_setcred (pamh, PAM_ESTABLISH_CRED); | ||
138 | } | ||
139 | while (0); | ||
140 | |||
141 | pam_end (pamh, PAM_SUCCESS); | ||
142 | |||
143 | switch (pamerror) | ||
144 | { | ||
145 | case PAM_SUCCESS: | ||
146 | return 0; | ||
147 | |||
148 | case PAM_AUTH_ERR: | ||
149 | return -1; | ||
150 | } | ||
151 | |||
152 | //FIXME _log(L_ERR, 0, "PAM authentication error"); | ||
153 | return -1; | ||
154 | } | ||
155 | |||
156 | static int | ||
157 | is_group_member (pies_identity_provider_t p, | ||
158 | pies_identity_t id, char * const * groups) | ||
159 | { | ||
160 | struct group *gr; | ||
161 | int result = 0; | ||
162 | |||
163 | setgrent (); | ||
164 | while ((gr = getgrent ())) | ||
165 | { | ||
166 | char **p; | ||
167 | |||
168 | if (!is_array_member (groups, gr->gr_name)) | ||
169 | continue; | ||
170 | |||
171 | for (p = gr->gr_mem; *p; p++) | ||
172 | if (strcmp (*p, id->username) == 0) | ||
173 | { | ||
174 | result = 1; | ||
175 | break; | ||
176 | } | ||
177 | } | ||
178 | endgrent (); | ||
179 | return result; | ||
180 | } | ||
181 | |||
182 | static struct grecs_keyword pam_kw[] = { | ||
183 | { "type", "'pam", "Set mechanism type", grecs_type_null }, | ||
184 | { "service", NULL, "Service name", | ||
185 | grecs_type_string, GRECS_DFLT, NULL, | ||
186 | offsetof(struct pam_identity_provider_data, service) }, | ||
187 | { NULL } | ||
188 | }; | ||
189 | |||
190 | static int | ||
191 | configure (struct grecs_node *node, pies_identity_provider_t provider) | ||
192 | { | ||
193 | int i; | ||
194 | struct pam_identity_provider_data *data = xcalloc (1, sizeof (*data)); | ||
195 | provider->data = data; | ||
196 | for (i = 0; pam_kw[i].ident; i++) | ||
197 | pam_kw[i].varptr = data; | ||
198 | if (grecs_tree_process (node->down, pam_kw)) | ||
199 | { | ||
200 | //FIXME: memory leak | ||
201 | return -1; | ||
202 | } | ||
203 | provider->locus = node->locus; | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static void | ||
208 | confhelp (void) | ||
209 | { | ||
210 | static struct grecs_keyword top[] = { | ||
211 | { "identity-provider", "name: string", | ||
212 | "Configuration for system identity provider", | ||
213 | grecs_type_section, GRECS_INAC, NULL, 0, NULL, NULL, | ||
214 | pam_kw }, | ||
215 | { NULL } | ||
216 | }; | ||
217 | grecs_print_statement_array (top, 1, 0, stdout); | ||
218 | } | ||
219 | |||
220 | struct pies_identity_mechanism pam_identity_mechanism = { | ||
221 | "pam", | ||
222 | authenticate, | ||
223 | is_group_member, | ||
224 | NULL, | ||
225 | configure, | ||
226 | confhelp | ||
227 | }; | ||
228 | |||
229 | |||
230 | |||