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 
15 WvPam::WvPam(WvStringParm _appname)
16  : log("PAM Auth", WvLog::Info), appname(_appname)
17 {
18  err.seterr("Compiled without PAM Support!\n");
19 }
20 
21 
22 WvPam::WvPam(WvStringParm _appname, WvStringParm rhost,
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 
30 WvPam::~WvPam()
31 {
32 }
33 
34 bool WvPam::authenticate(WvStringParm rhost, WvStringParm user, WvStringParm password)
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 
59 class WvPamData
60 {
61 public:
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
79 int noconv(int num_msg, struct pam_message **msgm,
80  struct pam_response **response, void *userdata)
81 #else
82 int 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
95 static int passconv(int num_msg, struct pam_message **msgm,
96  struct pam_response **response, void *userdata)
97 #else
98 static 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 
114 WvPam::WvPam(WvStringParm _appname)
115  : log("PAM Auth", WvLog::Info), appname(_appname)
116 {
117  init();
118 }
119 
120 
121 WvPam::WvPam(WvStringParm _appname, WvStringParm rhost,
122  WvStringParm user, WvStringParm password)
123  : log("PAM Auth", WvLog::Info), appname(_appname)
124 {
125  if (init())
126  authenticate(rhost, user, password);
127 }
128 
129 WvPam::~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 
140 bool 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 
156 bool WvPam::authenticate(WvStringParm rhost, WvStringParm user, WvStringParm password)
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 
238 bool 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 
257 WvString WvPam::getuser() const
258 {
259  return d->user;
260 }
261 
262 
263 void 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
WvPam::getuser
WvString getuser() const
Get the user's name.
Definition: wvpam.cc:39
WvPam::err
WvError err
Check here to see if the user is validated or not.
Definition: wvpam.h:74
WvErrorBase::seterr
virtual void seterr(int _errnum)
Set the errnum variable – we have an error.
Definition: wverror.cc:144
WvPam::getgroups
void getgroups(WvStringList &groups) const
Get the groups that the currently sessioned user is logged in with.
Definition: wvpam.cc:45
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvErrorBase::noerr
void noerr()
Reset our error state - there's no error condition anymore.
Definition: wverror.h:78
WvLog
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition: wvlog.h:56
group
Definition: argp-parse.c:204
WvPam::WvPam
WvPam(WvStringParm svcname)
Start up PAM (presumably you will want to call authenticate() later.
Definition: wvpam.cc:15
WvPam::authenticate
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
WvStringList
This is a WvList of WvStrings, and is a really handy way to parse strings.
Definition: wvstringlist.h:27