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