
Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 20/01/2015 06:45, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@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-using-...
Signed-off-by: Royce Lv <lvroyce@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):