From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
When accessing kimchi server url with authentication every time(no session),
kimchi server will deny service after about 1000 requests.
"GET /storagepools/default/storagevolumes/ HTTP/1.0" 200 563 ""
""
Failed to run command: id -Gn royce. [Errno 24] Too many open files
After tracking with 'lsof', "eventfd" handler leak for kimchi server.
This is because when using pam_start() to generate a pam file handler,
it needs to be closed by pam_end().
Unfortunately, PyPAM module only export start() function without end().
This patch workaround leak by putting PAM authentication in
a sub process, so that when it finishes execution,
its opening file handler will be closed by system automatically.
REF:
http://linux.die.net/man/3/pam_end
http://stackoverflow.com/questions/5125245/how-to-authenticate-a-user-usi...
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
---
src/kimchi/auth.py | 65 +++++++++++++++++++++++++++++++-----------------------
1 file changed, 37 insertions(+), 28 deletions(-)
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py
index 22c5c81..8cdbe76 100644
--- a/src/kimchi/auth.py
+++ b/src/kimchi/auth.py
@@ -145,35 +145,44 @@ class PAMUser(User):
@staticmethod
def authenticate(username, password, service="passwd"):
'''Returns True if authenticate is OK via PAM.'''
- def _pam_conv(auth, query_list, userData=None):
- resp = []
- for i in range(len(query_list)):
- query, qtype = query_list[i]
- if qtype == PAM.PAM_PROMPT_ECHO_ON:
- resp.append((username, 0))
- elif qtype == PAM.PAM_PROMPT_ECHO_OFF:
- resp.append((password, 0))
- elif qtype == PAM.PAM_PROMPT_ERROR_MSG:
- cherrypy.log.error_log.error(
- "PAM authenticate prompt error: %s" % query)
- resp.append(('', 0))
- elif qtype == PAM.PAM_PROMPT_TEXT_INFO:
- resp.append(('', 0))
- else:
- return None
- return resp
-
- auth = PAM.pam()
- auth.start(service)
- auth.set_item(PAM.PAM_USER, username)
- auth.set_item(PAM.PAM_CONV, _pam_conv)
- try:
- auth.authenticate()
- except PAM.error, (resp, code):
- msg_args = {'username': username, 'code': code}
- raise OperationFailed("KCHAUTH0001E", msg_args)
+ def _auth(result):
+ def _pam_conv(auth, query_list, userData=None):
+ resp = []
+ for i in range(len(query_list)):
+ query, qtype = query_list[i]
+ if qtype == PAM.PAM_PROMPT_ECHO_ON:
+ resp.append((username, 0))
+ elif qtype == PAM.PAM_PROMPT_ECHO_OFF:
+ resp.append((password, 0))
+ elif qtype == PAM.PAM_PROMPT_ERROR_MSG:
+ cherrypy.log.error_log.error(
+ "PAM authenticate prompt error: %s" % query)
+ resp.append(('', 0))
+ elif qtype == PAM.PAM_PROMPT_TEXT_INFO:
+ resp.append(('', 0))
+ else:
+ return None
+ return resp
- return True
+ result.value = False
+ auth = PAM.pam()
+ auth.start(service)
+ auth.set_item(PAM.PAM_USER, username)
+ auth.set_item(PAM.PAM_CONV, _pam_conv)
+ try:
+ auth.authenticate()
+ except PAM.error, (resp, code):
+ msg_args = {'username': username, 'code': code}
+ raise OperationFailed("KCHAUTH0001E", msg_args)
+
+ result.value = True
+
+ result = multiprocessing.Value('i', 0, lock=False)
+ p = multiprocessing.Process(target=_auth, args=(result, ))
+ p.start()
+ p.join()
+
+ return result.value
class LDAPUser(User):
--
1.9.3