Remoto - VFS: VFS_pam.cpp Source File
Remoto - VFS
VFS_pam.cpp
Go to the documentation of this file.
1 
2 #include <pwd.h>
3 #include <grp.h>
4 #include <unistd.h>
5 
6 #include "VFS.h"
7 #include "VFS_pam.h"
8 
9 //http://www.linux-pam.org/Linux-PAM-html/adg-introduction-synopsis.html
10 #include <security/pam_appl.h>
11 
12 VFS_pam::VFS_pam(QString service, QString group, bool debug)
13 : VFS_auth()
14 , _service(service)
15 , _group(group.toLower())
16 , _debug(debug)
17 {
18  /*
19  QJsonObject d;
20  d["username"] = "test";
21  d["password"] = "test";
22  d["type"] = "default";
23 
24  VFS_request r(VFS_request::read,this,"","",QJsonDocument(d));
25  read(&r);
26  ls(&r);
27  */
28 }
29 
31 {
32 }
33 
35 {
36  QMutexLocker l(&_lock);
37 
38  QJsonObject d = r->_data.object();
39 
40  bool auth = r->_path == ""; //if the path is empty we're authenticating. Otherwise we're just reading the data.
41 
42  QString user = auth ? d["username"].toString("") : r->_path;
43  QString pass = d["password"].toString("");
44 
45  if (_debug)
46  printf("user:'%s' auth:%d service:'%s'\n",qUtf8Printable(user),auth,qUtf8Printable(_service));
47 
48  if (user.isEmpty())
49  {
50  r->_reason = "Username cannot be empty.";
51  r->_success = false;
52  return;
53  }
54 
55  if ( !auth || validUserPass(user, pass) )
56  {
57  if (_debug) VFS::WARN("AUTHENTICATED!");
58 
59  //uid_t uid = getuid();
60  //struct passwd *pw = getpwuid(uid);
61  struct passwd *pw = getpwnam( qUtf8Printable(user) );
62  QJsonArray groups = getGroups(pw);
63 
64  if (_group.isEmpty() || groups.contains(_group)) //only accept if our user is in our group, or no group is specified
65  {
66  d["username"] = user;
67  d["uidnumber"] = (int) pw->pw_uid;
68 
69  QString realname(pw->pw_gecos);
70  if (realname.isEmpty())
71  realname = user;
72  d["realname"] = realname;
73 
74  d["groups"] = groups;
75 
76  //d["type"] = type;
77  //d["authpath"] = d["authpath"];
78  //d["parameters"] = d["parameters"];
79 
80  r->_data.setObject(d);
81  r->_success = true;
82  return;
83  }
84  else
85  {
86  if (_debug)
87  VFS::WARN("User was authenticated, but did not belong to group '"+_group+"'");
88  }
89  }
90  else
91  if (_debug) VFS::ERROR("NOT AUTHENTICATED: "+user);
92 
93  r->_success = false;
94 }
95 
97 {
98  /*
99  we need to provide for all users:
100  {
101  "username": username,
102  "uidnumber": uid,
103  "realname": realname
104  }
105  */
106 
107  if (_debug)
108  printf("filter group: %s\n",qUtf8Printable(_group));
109 
110  QJsonObject o;
111  struct passwd *pw;
112 
113  setpwent();
114  while((pw = getpwent()) != nullptr)
115  {
116  if (_debug)
117  printf("name=%s uid=%d group=%d real=%s\n",pw->pw_name,pw->pw_uid,pw->pw_gid,pw->pw_gecos);
118 
119  // kind of inefficient that we have to fetch each user,
120  // then fetch all groups for each user,
121  // then make sure that user is in the group that we want,
122  // but there doesn't seem to be a get all "users in group" mechanism
123 
124  if (_group.isEmpty() || getGroups(pw).contains(_group))
125  {
126  QString username(pw->pw_name);
127  int uidnumber((int)pw->pw_uid);
128  QString realname(pw->pw_gecos);
129  if (realname.isEmpty())
130  realname = username;
131 
132  o[username] = QJsonObject {
133  { "username", username },
134  { "uidnumber", uidnumber },
135  { "realname", realname }
136  };
137  }
138  }
139  endpwent();
140 
141  QJsonObject u = r->_data.object();
142  u[r->_initialPath] = o;
143  r->_data.setObject(u);
144  r->_success = true;
145 
146  if (_debug)
147  printf("USERS: %s",qUtf8Printable( r->_data.toJson() ));
148 }
149 
150 int pam_conv_free( pam_response *resp, int nresp )
151 {
152  for (int i=0; i<nresp; ++i)
153  {
154  if (resp[i].resp != nullptr)
155  {
156  memset(resp[i].resp, 0, strlen(resp[i].resp));
157  free(resp[i].resp);
158  }
159  }
160 
161  memset(resp, 0, (unsigned long) nresp * sizeof *resp);
162 
163  return PAM_CONV_ERR;
164 }
165 
167  int num_msg,
168  const struct pam_message **msg,
169  struct pam_response **resp,
170  void *appdata
171 )
172 {
173  if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG)
174  return PAM_CONV_ERR;
175 
176  struct pam_response *r;
177  if ((r = (pam_response *)calloc((size_t) num_msg, sizeof *r)) == nullptr)
178  return PAM_BUF_ERR;
179 
180  QJsonObject *data = (QJsonObject *) appdata;
181 
182  for (int i=0; i<num_msg; i++)
183  {
184  r[i].resp = nullptr;
185  r[i].resp_retcode = 0;
186 
187  switch(msg[i]->msg_style)
188  {
189  case PAM_PROMPT_ECHO_ON:
190  case PAM_PROMPT_ECHO_OFF:
191  //msg[i]->msg contains a prompt
192  //r->resp = getpass(msg[i]->msg);
193  r[i].resp = strdup(qUtf8Printable((*data)["password"].toString()));
194  r[i].resp_retcode = PAM_SUCCESS;
195  break;
196 
197  case PAM_ERROR_MSG:
198  //fprintf(stderr, "%s\n", m->msg);
199  VFS::ERROR(QString("%1").arg(msg[i]->msg),0,"pam");
200  r[i].resp_retcode = PAM_SUCCESS;
201  break;
202 
203  case PAM_TEXT_INFO:
204  //fprintf(stdout, "%s\n", m->msg);
205  VFS::LOG(QString("%1").arg(msg[i]->msg),0,"pam");
206  r[i].resp_retcode = PAM_SUCCESS;
207  break;
208 
209  default:
210  //free(r);
211  VFS::ERROR(QString("Unknown msg_style: %1").arg(msg[i]->msg),0,"pam");
212  *resp = nullptr;
213  return pam_conv_free(r,num_msg);
214  }
215  }
216 
217  *resp = r;
218  return PAM_SUCCESS;
219 }
220 
221 bool VFS_pam::validUserPass(QString user, QString pass)
222 {
223  bool valid = false;
224 
225  QJsonObject d;
226  d["username"] = user;
227  d["password"] = pass;
228 
229  pam_handle_t *pamh=nullptr;
230  int retval;
231 
232  struct pam_conv pam_conversation = {
233  pam_conv_func, // conversation function
234  &d // application_data
235  };
236 
237  retval = pam_start( qUtf8Printable(_service), qUtf8Printable(user), &pam_conversation, &pamh );
238 
239  #ifdef HAVE_PAM_FAIL_DELAY
240  if (retval == PAM_SUCCESS) //this does not override the need for a service file in /etc/pam.d -- see reference in the examples folder
241  retval = pam_fail_delay (pamh, 0 ); // micro-seconds
242  #endif /* HAVE_PAM_FAIL_DELAY */
243 
244  if (retval == PAM_SUCCESS)
245  retval = pam_authenticate(pamh, 0);
246 
247  if (_debug && retval == PAM_USER_UNKNOWN)
248  VFS::WARN("Unknown user: "+user,0,className());
249 
250  if (retval == PAM_SUCCESS)
251  retval = pam_acct_mgmt(pamh, 0);
252 
253  if (retval == PAM_SUCCESS)
254  valid = true;
255  //else
256  //get and return a reason from the pam_conversation?
257 
258  if ( pam_end(pamh,retval) != PAM_SUCCESS )
259  {
260  pamh = nullptr;
261  VFS::ERROR("Failed to release authenticator (2)",0,className());
262  }
263 
264  return valid;
265 }
266 
267 QJsonArray VFS_pam::getGroups(passwd *pw)
268 {
269  QJsonArray groupList;
270 
271  if(pw == nullptr)
272  {
273  if (_debug)
274  VFS::ERROR("getGroups null ps!",0,className());
275  }
276  else
277  {
278  #ifdef __APPLE__
279  static int groups[NGROUPS_MAX];
280  #else
281  static gid_t groups[NGROUPS_MAX];
282  #endif
283 
284  int ngroups = NGROUPS_MAX;
285  if (getgrouplist(pw->pw_name, (int) pw->pw_gid, groups, &ngroups) > -1)
286  {
287  for (int i = 0; i < ngroups; i++)
288  {
289  //printf("GROUP %d\n",groups[i]);
290 
291  struct group* gr = getgrgid((gid_t)groups[i]);
292  if(gr == nullptr)
293  {
294  //perror("getgrgid error: ");
295  if (_debug)
296  VFS::ERROR("getgrid error!");
297  }
298  else
299  {
300  if (_debug)
301  printf("%s\n",gr->gr_name);
302 
303  QString gname(gr->gr_name);
304 
305  if (!gname.startsWith("_"))
306  groupList << gname.toLower();
307  }
308  }
309  }
310  }
311 
312  return groupList;
313 }
314 
316 {
317  return "Service: "+_service+" "+"Group: "+_group;
318 }
int pam_conv_func(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata)
Definition: VFS_pam.cpp:166
int pam_conv_free(pam_response *resp, int nresp)
Definition: VFS_pam.cpp:150
The base class for authenticating users.
Definition: VFS_auth.h:7
QString className()
Return the class name of a node.
Definition: VFS_node.cpp:2039
QMutex _lock
A recursive mutex that is local to this node.
Definition: VFS_node.h:178
virtual void read(VFS_request *r)
Return the data contents of this node, or if it's a container call ls()
Definition: VFS_pam.cpp:34
QJsonArray getGroups(passwd *pw)
Definition: VFS_pam.cpp:267
Q_INVOKABLE VFS_pam(QString service, QString group, bool debug=false)
Definition: VFS_pam.cpp:12
virtual void ls(VFS_request *r)
List the contents of this node.
Definition: VFS_pam.cpp:96
bool validUserPass(QString user, QString password)
Definition: VFS_pam.cpp:221
QString _service
the pam service file name
Definition: VFS_pam.h:23
virtual QString reportDetails()
Additional details for a generated report.
Definition: VFS_pam.cpp:315
bool _debug
Definition: VFS_pam.h:25
QString _group
the group (or empty) a user must belong to and also the group whose members will be returned by ls()
Definition: VFS_pam.h:24
virtual ~VFS_pam()
Definition: VFS_pam.cpp:30
The base class for all requests between nodes.
Definition: VFS_node.h:54
QString _initialPath
the target path when the request was made (relative to the responder)
Definition: VFS_node.h:93
QString _reason
if something (probably bad) happened, this is the reason
Definition: VFS_node.h:108
QString _path
the target path remnant... the remaining path element once the request has found its target
Definition: VFS_node.h:95
bool _success
if the request was successfully completed
Definition: VFS_node.h:107
QJsonDocument _data
the request payload
Definition: VFS_node.h:102
static void LOG(QString message, int level=0, QString user="server")
Send a message to the VFS::_messages VFS_stream.
Definition: VFS.cpp:209
static void ERROR(QString message, int level=0, QString user="server")
Send a message to the VFS::_errors VFS_stream.
Definition: VFS.cpp:307
static void WARN(QString message, int level=0, QString user="server")
Send a message to the VFS::_warnings VFS_stream.
Definition: VFS.cpp:258
setter user
a setter DOCME