/* pam.c - pam processing routines Copyright (C) 2009 Howard Chu Copyright (C) 2009-2014 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE /* for crypt_r(3) in crypt.h */ #include #include "config.h" #include #include #include #ifdef HAVE_STDINT_H #include #endif /* HAVE_STDINT_H */ #include #include #include "common.h" #include "log.h" #include "cfg.h" #include "common/dict.h" #include "common/expr.h" struct authc { int authc_rc; char *username; int authz_rc; char authz_msg[BUFLEN_MESSAGE]; }; struct authz { int authz_rc; char authz_msg[BUFLEN_MESSAGE]; }; static int check_password(const char *password, const char *hash) { int ret; struct crypt_data data; data.initialized = 0; ret = (strcmp(crypt_r(password, hash, &data), hash) == 0); memset(&data, 0, sizeof(data)); return ret; } static int check_password_age(struct session *session, const char *username, char *authzmsg, size_t authzmsgsz, bool check_maxdays, bool check_mindays) { /* hackers.git doesn't use aging features */ return NSLCD_PAM_SUCCESS; } /* check authentication credentials of the user */ NSLCD_HANDLE_UID(PAM, AUTHC ,/* request data */ struct { char username[BUFLEN_NAME]; char service[BUFLEN_NAME]; char ruser[BUFLEN_NAME]; char rhost[HOST_NAME_MAX+1]; char tty[64]; char password[BUFLEN_PASSWORD]; } ,/* search data */ struct authc ,/* entry type */ struct authc ,/* int read(TFILE *fp, *req) */ READ_STRING(fp, req->username); READ_STRING(fp, req->service); READ_STRING(fp, req->ruser); READ_STRING(fp, req->rhost); READ_STRING(fp, req->tty); READ_STRING(fp, req->password); log_setrequest("authc=\"%s\"", req->username); log_log(LOG_DEBUG, "nslcd_pam_authc(\"%s\",\"%s\",\"%s\")", req->username, req->service, req->password[0] ? "***" : ""); return 0; ,/* check */ if (!isvalidname(req->username)) { log_log(LOG_WARNING, "request denied by validnames option"); return -1; } return 0; ,/* search(*session, *req, *searchdat, **entry) */ if (searchdat->username != NULL) { *entry = NULL; return 0; } struct passwd *user = NULL; for (size_t i = 0; i < session->cnt; i++) { if (session->users[i].pw_uid != UID_INVALID && STR_CMP(req->username, session->users[i].pw_name)==0) { user = &(session->users[i]); break; } } if (user == NULL) { *entry = NULL; return 0; } *entry = searchdat; (*entry)->authz_msg[0] = '\0'; /* try authentication */ (*entry)->authc_rc = check_password(req->password, NULL /* TODO */) ? NSLCD_PAM_SUCCESS : NSLCD_PAM_AUTH_ERR; (*entry)->authz_rc = (*entry)->authc_rc; if ((*entry)->authz_rc == NSLCD_PAM_SUCCESS) { (*entry)->username = user->pw_name; /* perform shadow attribute checks */ (*entry)->authz_rc = check_password_age(session, user->pw_name, (*entry)->authz_msg, sizeof((*entry)->authz_msg), true, false); } return 0; ,/* write(TFILE *fp, *entry) */ WRITE_INT32( fp, entry->authc_rc); WRITE_STRING(fp, entry->username); WRITE_INT32( fp, entry->authz_rc); WRITE_STRING(fp, entry->authz_msg); return 0; ,/* cleanup(*req, *searchdat) */ memset(req->password, 0, sizeof(req->password)); ) /* check authorisation of the user */ NSLCD_HANDLE(PAM, AUTHZ ,/* request data */ struct { char username[BUFLEN_NAME]; char service[BUFLEN_NAME]; char ruser[BUFLEN_NAME]; char rhost[HOST_NAME_MAX+1]; char tty[64]; } ,/* search data */ struct { int cnt; struct authz entry; } ,/* entry type */ struct authz ,/* int read(TFILE *fp, *req) */ READ_STRING(fp, req->username); READ_STRING(fp, req->service); READ_STRING(fp, req->ruser); READ_STRING(fp, req->rhost); READ_STRING(fp, req->tty); log_setrequest("authz=\"%s\"", req->username); log_log(LOG_DEBUG, "nslcd_pam_authz(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\")", req->username, req->service, req->ruser, req->rhost, req->tty); return 0; ,/* check */ return 0; ,/* search(*session, *req, *searchdat, *entry) */ if (searchdat->cnt++ != 0) { *entry = NULL; return 0; } struct passwd *user = NULL; for (size_t i = 0; i < session->cnt; i++) { if (session->users[i].pw_uid != UID_INVALID && STR_CMP(req->username, session->users[i].pw_name)==0) { user = &(session->users[i]); break; } } if (user == NULL) { *entry = NULL; return 0; } *entry = &(searchdat->entry); /* Parabola doesn't have any weird reasons for authorization to suddenly fail */ if (0) { (*entry)->authz_rc = NSLCD_PAM_PERM_DENIED; strcpy((*entry)->authz_msg, "hackers.git authorization check failed"); } else { /* perform shadow attribute checks */ (*entry)->authz_rc = check_password_age(session, req->username, (*entry)->authz_msg, sizeof((*entry)->authz_msg), false, false); } return 0; ,/* write(TFILE *fp, tentry *entry) */ WRITE_INT32( fp, entry->authz_rc); WRITE_STRING(fp, entry->authz_msg); return 0; ,/* cleanup */ ) static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "01234567890"; NSLCD_HANDLE(PAM, SESS_O ,/* request data */ struct { char username[BUFLEN_NAME]; char service[BUFLEN_NAME]; char ruser[BUFLEN_NAME]; char rhost[HOST_NAME_MAX+1]; char tty[64]; } ,/* search data */ struct { char sessionid[25]; } ,/* entry type */ char ,/* int read(TFILE *fp, *req) */ READ_STRING(fp, req->username); READ_STRING(fp, req->service); READ_STRING(fp, req->ruser); READ_STRING(fp, req->rhost); READ_STRING(fp, req->tty); log_setrequest("sess_o=\"%s\"", req->username); return 0; ,/* check(*req) */ return 0; ,/* search(*session, *req, *searchdat, **entry) */ /* generate pseudo-random session id */ if (searchdat->sessionid[0] == '\0') { size_t i; for (i = 0; i < (sizeof(searchdat->sessionid) - 1); i++) searchdat->sessionid[i] = alphabet[rand() % (sizeof(alphabet) - 1)]; searchdat->sessionid[i] = '\0'; *entry = searchdat->sessionid; } else { *entry = NULL; } log_log(LOG_DEBUG, "nslcd_pam_sess_o(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"): %s", req->username, req->service, req->tty, req->rhost, req->ruser, searchdat->sessionid); return 0; ,/* write(TFILE *fp, *entry) */ WRITE_STRING(fp, entry); return 0; ,/* cleanup */ ) NSLCD_HANDLE(PAM, SESS_C ,/* request data */ struct { char username[BUFLEN_NAME]; char service[BUFLEN_NAME]; char ruser[BUFLEN_NAME]; char rhost[HOST_NAME_MAX+1]; char tty[64]; char sessionid[64]; } ,/* search data */ struct { int cnt; } ,/* entry type */ void ,/* int read(TFILE *fp, *req) */ READ_STRING(fp, req->username); READ_STRING(fp, req->service); READ_STRING(fp, req->ruser); READ_STRING(fp, req->rhost); READ_STRING(fp, req->tty); READ_STRING(fp, req->sessionid); log_setrequest("sess_c=\"%s\"", req->username); log_log(LOG_DEBUG, "nslcd_pam_sess_c(\"%s\",\"%s\",%s)", req->username, req->service, req->sessionid); return 0; ,/* check(*req) */ return 0; ,/* search(*session, *req, *searchdat, **entry) */ *entry = (searchdat->cnt++ == 0) ? (void*)1 : NULL; return 0; ,/* int write(TFILE *fp, tentry *entry) */ return 0; ,/* cleanup */ )