1 /* This file is part of GNU Dico.
2 Copyright (C) 2008-2020 Sergey Poznyakoff
3
4 GNU Dico 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 Dico 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 Dico. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <dico.h>
19 #include <appi18n.h>
20 #include <wordsplit.h>
21
22 #include <errno.h>
23 #include <security/pam_appl.h>
24
25 static char *service = "dicod";
26
27 #define COPY_STRING(s) (s) ? strdup(s) : NULL
28
29 #define overwrite_and_free(ptr) \
30 do { \
31 char *s = ptr; \
32 while (*s) \
33 *s++ = 0; \
34 } while (0)
35
36
37 #ifndef PAM_AUTHTOK_RECOVER_ERR
38 # define PAM_AUTHTOK_RECOVER_ERR PAM_CONV_ERR
39 #endif
40
41 struct pam_cred {
42 const char *user;
43 const char *pass;
44 };
45
46 static int
_dico_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)47 _dico_conv(int num_msg, const struct pam_message **msg,
48 struct pam_response **resp, void *appdata_ptr)
49 {
50 int status = PAM_SUCCESS;
51 int i;
52 struct pam_response *reply = NULL;
53 struct pam_cred *cred = appdata_ptr;
54
55 reply = calloc(num_msg, sizeof(*reply));
56 if (!reply)
57 return PAM_CONV_ERR;
58
59 for (i = 0; i < num_msg && status == PAM_SUCCESS; i++) {
60 switch (msg[i]->msg_style) {
61 case PAM_PROMPT_ECHO_ON:
62 reply[i].resp_retcode = PAM_SUCCESS;
63 reply[i].resp = COPY_STRING(cred->user);
64 /* PAM frees resp */
65 break;
66
67 case PAM_PROMPT_ECHO_OFF:
68 if (cred->pass) {
69 reply[i].resp_retcode = PAM_SUCCESS;
70 reply[i].resp = COPY_STRING(cred->pass);
71 /* PAM frees resp */
72 } else
73 status = PAM_AUTHTOK_RECOVER_ERR;
74 break;
75
76 case PAM_TEXT_INFO:
77 case PAM_ERROR_MSG:
78 reply[i].resp_retcode = PAM_SUCCESS;
79 reply[i].resp = NULL;
80 break;
81
82 default:
83 status = PAM_CONV_ERR;
84 }
85 }
86
87 if (status != PAM_SUCCESS) {
88 for (i = 0; i < num_msg; i++)
89 if (reply[i].resp) {
90 switch (msg[i]->msg_style) {
91 case PAM_PROMPT_ECHO_ON:
92 case PAM_PROMPT_ECHO_OFF:
93 overwrite_and_free(reply[i].resp);
94 break;
95
96 case PAM_ERROR_MSG:
97 case PAM_TEXT_INFO:
98 free(reply[i].resp);
99 }
100 }
101 free(reply);
102 } else
103 *resp = reply;
104 return status;
105 }
106
107
108 static int
db_check_pass(void * handle,const char * pwres,const char * key,const char * pass)109 db_check_pass(void *handle, const char *pwres,
110 const char *key, const char *pass)
111 {
112 pam_handle_t *pamh;
113 int pamerror;
114 struct pam_cred cred = { key, pass };
115 struct pam_conv _dico_pam_conv = { &_dico_conv, &cred };
116
117 #define PAM_ERROR if (pamerror != PAM_SUCCESS) break;
118
119 do {
120 pamerror = pam_start(pwres ? pwres : service, key,
121 &_dico_pam_conv, &pamh);
122 PAM_ERROR;
123 pamerror = pam_authenticate(pamh, 0);
124 PAM_ERROR;
125 pamerror = pam_acct_mgmt(pamh, 0);
126 PAM_ERROR;
127 pamerror = pam_setcred(pamh, PAM_ESTABLISH_CRED);
128 } while(0);
129
130 pam_end(pamh, PAM_SUCCESS);
131
132 switch (pamerror) {
133 case PAM_SUCCESS:
134 return 0;
135 case PAM_AUTH_ERR:
136 return 1;
137 }
138 dico_log(L_ERR, 0, "PAM authentication error");
139 return 1;
140 }
141
142 static int
db_get_groups(void * handle,const char * qgr,const char * key,dico_list_t * pgroups)143 db_get_groups(void *handle, const char *qgr, const char *key,
144 dico_list_t *pgroups)
145 {
146 *pgroups = NULL;
147 return 0;
148 }
149
150
151
152 static struct dico_udb_def pam_udb_def = {
153 "pam",
154 NULL,
155 NULL,
156 NULL,
157 db_get_groups,
158 db_check_pass
159 };
160
161
162 static int
dico_pam_init(int argc,char ** argv)163 dico_pam_init(int argc, char **argv)
164 {
165 struct dico_option init_option[] = {
166 { DICO_OPTSTR(service), dico_opt_string, &service },
167 { NULL }
168 };
169 if (dico_parseopt(init_option, argc, argv, 0, NULL))
170 return -1;
171 return dico_udb_define(&pam_udb_def);
172 }
173
174
175 struct dico_database_module DICO_EXPORT(pam, module) = {
176 DICO_MODULE_VERSION,
177 DICO_CAPA_NODB,
178 dico_pam_init,
179 };
180