WvStreams
wvpam.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2003 Net Integration Technologies, Inc.
4 *
5 * A WvStream that authenticates with PAM before allowing any reading or
6 * writing. See wvpam.h.
7 */
8#include "wvlog.h"
9#include "wvpam.h"
10#include "wvautoconf.h"
11
12// If PAM not installed at compile time, stub this out
13#ifndef HAVE_SECURITY_PAM_APPL_H
14
16 : log("PAM Auth", WvLog::Info), appname(_appname)
17{
18 err.seterr("Compiled without PAM Support!\n");
19}
20
21
23 WvStringParm user, WvStringParm password)
24 : log("PAM Auth", WvLog::Info), appname(_appname)
25{
26 err.seterr("Compiled without PAM Support!\n");
27}
28
29
30WvPam::~WvPam()
31{
32}
33
35{
36 return false;
37}
38
40{
41 return WvString::null;
42}
43
44
46{
47}
48
49#else // HAVE_SECURITY_PAM_APPL_H
50
51#include <security/pam_appl.h>
52#include <sys/types.h>
53#include <pwd.h>
54#include <grp.h>
55
56#include "wvaddr.h"
57
58
59class WvPamData
60{
61public:
62 pam_handle_t *pamh;
63 int status;
64 WvString failmsg, user;
65 WvStringList groups;
66
67 WvPamData()
68 : pamh(NULL), status(PAM_SUCCESS), user("")
69 { }
70
71 WvPamData(WvStringParm _failmsg)
72 : pamh(NULL), status(PAM_SUCCESS), failmsg(_failmsg)
73 { }
74};
75
76
78#if HAVE_BROKEN_PAM
79int noconv(int num_msg, struct pam_message **msgm,
80 struct pam_response **response, void *userdata)
81#else
82int noconv(int num_msg, const struct pam_message **msgm,
83 struct pam_response **response, void *userdata)
84#endif
85{
86 // if you need to ask things, it won't work
87 return PAM_CONV_ERR;
88}
89
90
91// The password gets passed in from userdata, and we simply echo it back
92// out in the response... *sigh* This is because pam expects this function
93// to actually interact with the user, and get their password.
94#if HAVE_BROKEN_PAM
95static int passconv(int num_msg, struct pam_message **msgm,
96 struct pam_response **response, void *userdata)
97#else
98static int passconv(int num_msg, const struct pam_message **msgm,
99 struct pam_response **response, void *userdata)
100#endif
101{
102 struct pam_response *password_echo;
103
104 password_echo = (struct pam_response *)calloc(num_msg,
105 sizeof(struct pam_response));
106 password_echo->resp = (char *)userdata;
107 password_echo->resp_retcode = 0;
108
109 *response = password_echo;
110
111 return PAM_SUCCESS;
112}
113
115 : log("PAM Auth", WvLog::Info), appname(_appname)
116{
117 init();
118}
119
120
122 WvStringParm user, WvStringParm password)
123 : log("PAM Auth", WvLog::Info), appname(_appname)
124{
125 if (init())
126 authenticate(rhost, user, password);
127}
128
129WvPam::~WvPam()
130{
131 log(WvLog::Debug2, "Shutting down PAM Session for: %s\n", appname);
132 if (d->status == PAM_SUCCESS)
133 pam_close_session(d->pamh, 0);
134 pam_end(d->pamh, d->status);
135 d->groups.zap();
136 delete d;
137}
138
139
140bool WvPam::init()
141{
142 d = new WvPamData();
143 log(WvLog::Debug2, "Starting up PAM Session for: %s\n", appname);
144 err.seterr("Not yet authenticated...");
145
146 struct pam_conv c;
147 c.conv = noconv;
148 c.appdata_ptr = NULL;
149
150 d->pamh = NULL;
151 d->status = pam_start(appname, d->user, &c, &d->pamh);
152 if (check_pam_status("pam_start")) return true;
153 return false;
154}
155
157{
158 // Just in case...
159 assert(d);
160
161 if (!!rhost)
162 {
163 d->status = pam_set_item(d->pamh, PAM_RHOST, rhost);
164 if (!check_pam_status("rhost setup"))
165 return false;
166 }
167
168 if (!!user)
169 {
170 d->user = user;
171 d->status = pam_set_item(d->pamh, PAM_USER, user);
172 if (!check_pam_status("user setup"))
173 return false;
174 }
175
176 if (!!password)
177 {
178 struct pam_conv c;
179 c.conv = passconv;
180 c.appdata_ptr = strdup(password);
181 d->status = pam_set_item(d->pamh, PAM_CONV, &c);
182 if (!check_pam_status("conversation setup"))
183 return false;
184
185 d->status = pam_set_item(d->pamh, PAM_AUTHTOK, password);
186 if (!check_pam_status("password setup"))
187 return false;
188 }
189
190#if HAVE_BROKEN_PAM
191 void *x = NULL;
192#else
193 const void *x = NULL;
194#endif
195 d->status = pam_get_item(d->pamh, PAM_USER, &x);
196 if (!check_pam_status("get username"))
197 return false;
198 d->user = (const char *)x;
199 d->user.unique();
200
201 log("Starting Authentication for %s@%s\n", d->user, rhost);
202
203 d->status = pam_authenticate(d->pamh, PAM_DISALLOW_NULL_AUTHTOK | PAM_SILENT);
204 if (!check_pam_status("authentication")) return false;
205
206 d->status = pam_acct_mgmt(d->pamh, PAM_DISALLOW_NULL_AUTHTOK | PAM_SILENT);
207 if (!check_pam_status("account management")) return false;
208
209 d->status = pam_setcred(d->pamh, PAM_ESTABLISH_CRED);
210 if (!check_pam_status("credentials")) return false;
211
212 d->status = pam_open_session(d->pamh, 0);
213 if (!check_pam_status("session open")) return false;
214
215 // Grab the current user name (now that we've authenticated)
216 if (!d->user)
217 {
218#ifdef SOLARIS
219 void *x = NULL;
220#else
221 const void *x = NULL;
222#endif
223 d->status = pam_get_item(d->pamh, PAM_USER, &x);
224 if (!check_pam_status("get username")) return false;
225 d->user = (const char *)x;
226 d->user.unique();
227 }
228 log("Session open as user '%s'\n", d->user);
229
230 // If we made it here, we're clear of everything, and we can go
231 // to a no error status.
232 err.noerr();
233
234 return true;
235}
236
237
238bool WvPam::check_pam_status(WvStringParm s)
239{
240 if (d->status == PAM_SUCCESS)
241 {
242 log(WvLog::Debug2, "PAM %s succeeded.\n", s);
243 return true;
244 }
245 else
246 {
247 WvString msg("PAM %s failed: %s\n", s, pam_strerror(d->pamh, d->status));
248 log(WvLog::Info, msg);
249 err.seterr(msg);
250 d->user = WvString::null;
251 d->groups.zap();
252 return false;
253 }
254}
255
256
258{
259 return d->user;
260}
261
262
263void WvPam::getgroups(WvStringList &l) const
264{
265 assert(l.isempty());
266
267 // Cache after the first time...
268 if (d->groups.isempty())
269 {
270 setgrent();
271 struct group *gr;
272 while ((gr = getgrent()))
273 {
274 for (char **i = gr->gr_mem; *i != NULL; i++)
275 {
276 if (strcmp(*i, d->user) == 0)
277 {
278 d->groups.append(new WvString(gr->gr_name), true);
279 break;
280 }
281 }
282 }
283 endgrent();
284 }
285
286 WvStringList::Iter i(d->groups);
287 for (i.rewind(); i.next(); )
288 l.append(new WvString(*i), true);
289}
290
291
292
293
294
295
296#endif // HAVE_SECURITY_PAM_APPL_H
void noerr()
Reset our error state - there's no error condition anymore.
Definition wverror.h:78
virtual void seterr(int _errnum)
Set the errnum variable – we have an error.
Definition wverror.cc:144
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition wvstring.h:94
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition wvlog.h:57
WvString getuser() const
Get the user's name.
Definition wvpam.cc:39
void getgroups(WvStringList &groups) const
Get the groups that the currently sessioned user is logged in with.
Definition wvpam.cc:45
WvPam(WvStringParm svcname)
Start up PAM (presumably you will want to call authenticate() later.
Definition wvpam.cc:15
bool authenticate(WvStringParm rhost=WvString::null, WvStringParm user=WvString::null, WvStringParm password=WvString::null)
Authenticate the user from rhost with password.
Definition wvpam.cc:34
WvError err
Check here to see if the user is validated or not.
Definition wvpam.h:74
This is a WvList of WvStrings, and is a really handy way to parse strings.
WvString is an implementation of a simple and efficient printable-string class.
Definition wvstring.h:330