1 /*
2 * Copyright (c) 2001, 2002, 2004, 2005, 2006, 2008, 2009, 2010, 2011, 2012,
3 * 2013
4 * Inferno Nettverk A/S, Norway. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. The above copyright notice, this list of conditions and the following
10 * disclaimer must appear in all copies of the software, derivative works
11 * or modified versions, and any portions thereof, aswell as in all
12 * supporting documentation.
13 * 2. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by
16 * Inferno Nettverk A/S, Norway.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Inferno Nettverk A/S requests users of this software to return to
32 *
33 * Software Distribution Coordinator or sdc@inet.no
34 * Inferno Nettverk A/S
35 * Oslo Research Park
36 * Gaustadall�en 21
37 * NO-0349 Oslo
38 * Norway
39 *
40 * any improvements or extensions that they make and grant Inferno Nettverk A/S
41 * the rights to redistribute these changes.
42 *
43 */
44
45 /*
46 * Based on code originally from
47 * Patrick Bihan-Faou, MindStep Corporation, patrick@mindstep.com.
48 */
49
50 #include "common.h"
51
52 #if HAVE_PAM
53
54 static const char rcsid[] =
55 "$Id: auth_pam.c,v 1.100 2013/10/27 15:24:42 karls Exp $";
56
57 static int
58 pam_conversation(int msgc, const struct pam_message **msgv,
59 struct pam_response **rspv, void *authdata);
60 /*
61 * Called by the pam system to fetch username and password info.
62 */
63
64
65 int
pam_passwordcheck(s,src,dst,auth,emsg,emsgsize)66 pam_passwordcheck(s, src, dst, auth, emsg, emsgsize)
67 int s;
68 const struct sockaddr_storage *src, *dst;
69 const authmethod_pam_t *auth;
70 char *emsg;
71 size_t emsgsize;
72 {
73 const char *function = "pam_passwordcheck()";
74 authmethod_pam_t authdata = *auth;
75 struct pam_conv pamconv;
76 pam_handle_t *pamh;
77 size_t i;
78 char srcstr[MAXSOCKADDRSTRING], visbuf[MAXNAMELEN * 4];
79 int rc;
80
81 /*
82 * unfortunately we can not set password here, that needs to be set
83 * "from a module", i.e. in the conversion function, at least with
84 * one Linux pam implementation.
85 */
86 struct {
87 int item;
88 const char *itemname;
89 const void *value;
90 int printable;
91
92 } pamval[] = {
93 { (int)PAM_CONV, "PAM_CONV", &pamconv, 0 },
94 { (int)PAM_RHOST, "PAM_RHOST",
95 src == NULL ?
96 "" : sockaddr2string2(src, 0, srcstr, sizeof(srcstr)), 1 },
97 { (int)PAM_USER, "PAM_USER", (*auth->name == NUL) ?
98 DEFAULT_PAM_USER : (const char *)auth->name, 1 },
99 { (int)PAM_RUSER, "PAM_RUSER", DEFAULT_PAM_RUSER, 1 },
100 };
101
102 slog(LOG_DEBUG, "%s: src %s, user \"%s\", servicename \"%s\", emsgsize %ld",
103 function,
104 src == NULL ? "N/A" : sockaddr2string(src, NULL, 0),
105 str2vis((const char *)auth->name,
106 strlen((const char *)auth->name),
107 visbuf,
108 sizeof(visbuf)),
109 auth->servicename,
110 (long)emsgsize);
111
112 if (src == NULL) {
113 snprintf(emsg, emsgsize, "%s: NULL src address: not supported", function);
114 return -1;
115 }
116 /*
117 * Note: we can not save the state of pam after pam_start(3), as
118 * e.g. Solaris 5.11 pam does not allow setting PAM_SERVICE
119 * except during pam_start(3), while we may need to change it
120 * depending on the client/rule.
121 * Some Linux pam-implementations on the other hand can enter
122 * some sort of busy-loop if we don't call pam_end(3) ever so
123 * often.
124 *
125 * Therefor, disregard all possible optimization stuff for now and
126 * call pam_start(3) and pam_end(3) every time.
127 */
128
129 pamconv.conv = pam_conversation;
130 pamconv.appdata_ptr = &authdata;
131
132 sockd_priv(SOCKD_PRIV_PAM, PRIV_ON);
133 rc = pam_start(auth->servicename, NULL, &pamconv, &pamh);
134 sockd_priv(SOCKD_PRIV_PAM, PRIV_OFF);
135
136 if (rc != (int)PAM_SUCCESS) {
137 snprintf(emsg, emsgsize, "pam_start() failed: %s",
138 pam_strerror(pamh, rc));
139
140 return -1;
141 }
142
143 for (i = 0; i < ELEMENTS(pamval); ++i) {
144 if (pamval[i].printable) {
145 str2vis((const char *)pamval[i].value,
146 strlen((const char *)pamval[i].value),
147 visbuf,
148 sizeof(visbuf));
149
150 slog(LOG_DEBUG, "%s: setting item \"%s\" to value \"%s\"",
151 function, pamval[i].itemname, visbuf);
152 }
153 else
154 slog(LOG_DEBUG, "%s: setting item %s", function, pamval[i].itemname);
155
156 if ((rc = pam_set_item(pamh, pamval[i].item, pamval[i].value))
157 != (int)PAM_SUCCESS) {
158 snprintf(emsg, emsgsize, "pam_set_item(%s) to \"%s\" failed: %s",
159 pamval[i].itemname, visbuf, pam_strerror(pamh, rc));
160
161 pam_end(pamh, rc);
162 return -1;
163 }
164 }
165
166 sockd_priv(SOCKD_PRIV_PAM, PRIV_ON);
167
168 if ((rc = pam_authenticate(pamh, 0)) != (int)PAM_SUCCESS) {
169 sockd_priv(SOCKD_PRIV_PAM, PRIV_OFF);
170
171 slog(LOG_DEBUG, "%s: pam_authenticate() failed: %s",
172 function, pam_strerror(pamh, rc));
173
174 snprintf(emsg, emsgsize, "pam_authenticate() for user \"%s\" failed: %s",
175 *auth->name == NUL ?
176 "<no user specified>" : str2vis((const char *)auth->name,
177 strlen((const char *)auth->name),
178 visbuf,
179 sizeof(visbuf)),
180 pam_strerror(pamh, rc));
181
182 pam_end(pamh, rc);
183 return -1;
184 }
185
186 /* LINTED passing const, expecting non-const (PAM_SILENT) */
187 rc = pam_acct_mgmt(pamh, PAM_SILENT);
188
189 sockd_priv(SOCKD_PRIV_PAM, PRIV_OFF);
190
191 if (rc != PAM_SUCCESS) {
192 slog(LOG_DEBUG, "%s: pam_acct_mgmt() failed: %s",
193 function, pam_strerror(pamh, rc));
194
195 snprintf(emsg, emsgsize, "pam_acct_mgmt(): %s", pam_strerror(pamh, rc));
196
197 pam_end(pamh, rc);
198 return -1;
199 }
200
201 if ((rc = pam_end(pamh, rc)) != (int)PAM_SUCCESS)
202 swarnx("%s: strange ... pam_end() failed: %s",
203 function, pam_strerror(pamh, rc));
204
205 slog(LOG_DEBUG, "%s: pam authentication succeeded", function);
206 return 0;
207 }
208
209 static int
pam_conversation(msgc,msgv,rspv,authdata)210 pam_conversation(msgc, msgv, rspv, authdata)
211 int msgc;
212 const struct pam_message **msgv;
213 struct pam_response **rspv;
214 void *authdata;
215 {
216 const authmethod_pam_t *auth = authdata;
217 const char *function = "pam_conversation()";
218 int i, rc;
219
220 if (rspv == NULL || msgv == NULL || auth == NULL || msgc < 1) {
221 swarnx("%s: called with invalid/unexpected input", function);
222 return (int)PAM_CONV_ERR;
223 }
224
225 if (((*rspv) = malloc(msgc * sizeof(struct pam_response))) == NULL) {
226 swarn("%s: malloc(%d * %lu)",
227 function, msgc, (unsigned long)sizeof(struct pam_response));
228
229 return (int)PAM_CONV_ERR;
230 }
231
232 /* initialize all to NULL so we can easily free on error. */
233 for (i = 0; i < msgc; ++i) {
234 (*rspv)[i].resp_retcode = 0; /* according to sun not used, should be 0. */
235 (*rspv)[i].resp = NULL;
236 }
237
238 rc = (int)PAM_SUCCESS;
239 for (i = 0; i < msgc; ++i) {
240 slog(LOG_DEBUG, "%s: msg_style = %d", function, msgv[i]->msg_style);
241
242 switch(msgv[i]->msg_style) {
243 case PAM_PROMPT_ECHO_OFF:
244 if (((*rspv)[i].resp = strdup((const char *)auth->password))
245 == NULL) {
246 swarn("%s: strdup() of password, length %lu, failed",
247 function,
248 (unsigned long)strlen((const char *)auth->password));
249
250 rc = (int)PAM_CONV_ERR;
251 }
252 break;
253
254 case PAM_ERROR_MSG:
255 slog(LOG_INFO, "%s: got a pam error msg: %s",
256 function, msgv[i]->msg);
257 break;
258
259 case PAM_TEXT_INFO:
260 /*
261 * not expecting this, and where it has been seen (some versions
262 * of FreeBSD), the string has been empty.
263 * Seen it on Linux also. Don't know what it's for.
264 */
265 slog(LOG_DEBUG, "%s: got unexpected PAM_TEXT_INFO: \"%s\"",
266 function, msgv[i]->msg);
267 break;
268
269 default:
270 swarnx("%s: unknown msg_style %d, ignored ...",
271 function, msgv[i]->msg_style);
272 break;
273 }
274 }
275
276 if (rc != (int)PAM_SUCCESS) { /* failed; free the memory ourselves */
277 for (i = 0; i < msgc; ++i)
278 free((*rspv)[i].resp);
279
280 free(*rspv);
281 }
282
283 return rc;
284 }
285
286 #endif /* HAVE_PAM */
287