/* * System Security Services Daemon. NSS client interface * * Copyright (C) Simo Sorce 2007 * * This program 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* PASSWD database NSS interface */ #include #include #include #include #include #include #include #include "sss_cli.h" static struct sss_nss_getpwent_data { size_t len; size_t ptr; uint8_t *data; } sss_nss_getpwent_data; static void sss_nss_getpwent_data_clean(void) { if (sss_nss_getpwent_data.data != NULL) { free(sss_nss_getpwent_data.data); sss_nss_getpwent_data.data = NULL; } sss_nss_getpwent_data.len = 0; sss_nss_getpwent_data.ptr = 0; } /* GETPWNAM Request: * * 0-X: string with name * * GERTPWUID Request: * * 0-3: 32bit number with uid * * Replies: * * 0-3: 32bit unsigned number of results * 4-7: 32bit unsigned (reserved/padding) * For each result: * 0-3: 32bit number uid * 4-7: 32bit number gid * 8-X: sequence of 5, 0 terminated, strings (name, passwd, gecos, dir, shell) */ struct sss_nss_pw_rep { struct passwd *result; char *buffer; size_t buflen; }; static int sss_nss_getpw_readrep(struct sss_nss_pw_rep *pr, uint8_t *buf, size_t *len) { size_t i, slen, dlen; char *sbuf; if (*len < 13) { /* not enough space for data, bad packet */ return EBADMSG; } pr->result->pw_uid = ((uint32_t *)buf)[0]; pr->result->pw_gid = ((uint32_t *)buf)[1]; sbuf = (char *)&buf[8]; slen = *len - 8; dlen = pr->buflen; i = 0; pr->result->pw_name = &(pr->buffer[i]); while (slen > i && dlen > 0) { pr->buffer[i] = sbuf[i]; if (pr->buffer[i] == '\0') break; i++; dlen--; } if (slen <= i) { /* premature end of buf */ return EBADMSG; } if (dlen <= 0) { /* not enough memory */ return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ } i++; dlen--; pr->result->pw_passwd = &(pr->buffer[i]); while (slen > i && dlen > 0) { pr->buffer[i] = sbuf[i]; if (pr->buffer[i] == '\0') break; i++; dlen--; } if (slen <= i) { /* premature end of buf */ return EBADMSG; } if (dlen <= 0) { /* not enough memory */ return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ } i++; dlen--; pr->result->pw_gecos = &(pr->buffer[i]); while (slen > i && dlen > 0) { pr->buffer[i] = sbuf[i]; if (pr->buffer[i] == '\0') break; i++; dlen--; } if (slen <= i) { /* premature end of buf */ return EBADMSG; } if (dlen <= 0) { /* not enough memory */ return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ } i++; dlen--; pr->result->pw_dir = &(pr->buffer[i]); while (slen > i && dlen > 0) { pr->buffer[i] = sbuf[i]; if (pr->buffer[i] == '\0') break; i++; dlen--; } if (slen <= i) { /* premature end of buf */ return EBADMSG; } if (dlen <= 0) { /* not enough memory */ return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ } i++; dlen--; pr->result->pw_shell = &(pr->buffer[i]); while (slen > i && dlen > 0) { pr->buffer[i] = sbuf[i]; if (pr->buffer[i] == '\0') break; i++; dlen--; } if (slen <= i) { /* premature end of buf */ return EBADMSG; } if (dlen <= 0) { /* not enough memory */ return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ } *len = slen -i -1; return 0; } enum nss_status _nss_sss_getpwnam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errnop) { struct sss_cli_req_data rd; struct sss_nss_pw_rep pwrep; uint8_t *repbuf; size_t replen, len; enum nss_status nret; int ret; rd.len = strlen(name) + 1; rd.data = name; nret = sss_nss_make_request(SSS_NSS_GETPWNAM, &rd, &repbuf, &replen, errnop); if (nret != NSS_STATUS_SUCCESS) { return nret; } pwrep.result = result; pwrep.buffer = buffer; pwrep.buflen = buflen; /* no results if not found */ if (((uint32_t *)repbuf)[0] == 0) { free(repbuf); return NSS_STATUS_NOTFOUND; } /* only 1 result is accepted for this function */ if (((uint32_t *)repbuf)[0] != 1) { *errnop = EBADMSG; return NSS_STATUS_TRYAGAIN; } len = replen - 8; ret = sss_nss_getpw_readrep(&pwrep, repbuf+8, &len); free(repbuf); if (ret) { *errnop = ret; return NSS_STATUS_TRYAGAIN; } return NSS_STATUS_SUCCESS; } enum nss_status _nss_sss_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, size_t buflen, int *errnop) { struct sss_cli_req_data rd; struct sss_nss_pw_rep pwrep; uint8_t *repbuf; size_t replen, len; enum nss_status nret; uint32_t user_uid; int ret; user_uid = uid; rd.len = sizeof(uint32_t); rd.data = &user_uid; nret = sss_nss_make_request(SSS_NSS_GETPWUID, &rd, &repbuf, &replen, errnop); if (nret != NSS_STATUS_SUCCESS) { return nret; } pwrep.result = result; pwrep.buffer = buffer; pwrep.buflen = buflen; /* no results if not found */ if (((uint32_t *)repbuf)[0] == 0) { free(repbuf); return NSS_STATUS_NOTFOUND; } /* only 1 result is accepted for this function */ if (((uint32_t *)repbuf)[0] != 1) { *errnop = EBADMSG; return NSS_STATUS_TRYAGAIN; } len = replen - 8; ret = sss_nss_getpw_readrep(&pwrep, repbuf+8, &len); free(repbuf); if (ret) { *errnop = ret; return NSS_STATUS_TRYAGAIN; } return NSS_STATUS_SUCCESS; } enum nss_status _nss_sss_setpwent(void) { enum nss_status nret; int errnop; /* make sure we do not have leftovers, and release memory */ sss_nss_getpwent_data_clean(); nret = sss_nss_make_request(SSS_NSS_SETPWENT, NULL, NULL, NULL, &errnop); if (nret != NSS_STATUS_SUCCESS) { errno = errnop; return nret; } return NSS_STATUS_SUCCESS; } enum nss_status _nss_sss_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) { struct sss_cli_req_data rd; struct sss_nss_pw_rep pwrep; uint8_t *repbuf; size_t replen; enum nss_status nret; uint32_t num_entries; int ret; /* if there are leftovers return the next one */ if (sss_nss_getpwent_data.data != NULL && sss_nss_getpwent_data.ptr < sss_nss_getpwent_data.len) { repbuf = sss_nss_getpwent_data.data + sss_nss_getpwent_data.ptr; replen = sss_nss_getpwent_data.len - sss_nss_getpwent_data.ptr; pwrep.result = result; pwrep.buffer = buffer; pwrep.buflen = buflen; ret = sss_nss_getpw_readrep(&pwrep, repbuf, &replen); if (ret) { *errnop = ret; return NSS_STATUS_TRYAGAIN; } /* advance buffer pointer */ sss_nss_getpwent_data.ptr = sss_nss_getpwent_data.len - replen; return NSS_STATUS_SUCCESS; } /* release memory if any */ sss_nss_getpwent_data_clean(); /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */ num_entries = SSS_NSS_MAX_ENTRIES; rd.len = sizeof(uint32_t); rd.data = &num_entries; nret = sss_nss_make_request(SSS_NSS_GETPWENT, &rd, &repbuf, &replen, errnop); if (nret != NSS_STATUS_SUCCESS) { return nret; } /* no results if not found */ if ((((uint32_t *)repbuf)[0] == 0) || (replen - 8 == 0)) { free(repbuf); return NSS_STATUS_NOTFOUND; } sss_nss_getpwent_data.data = repbuf; sss_nss_getpwent_data.len = replen; sss_nss_getpwent_data.ptr = 8; /* skip metadata fields */ /* call again ourselves, this will return the first result */ return _nss_sss_getpwent_r(result, buffer, buflen, errnop); } enum nss_status _nss_sss_endpwent(void) { enum nss_status nret; int errnop; /* make sure we do not have leftovers, and release memory */ sss_nss_getpwent_data_clean(); nret = sss_nss_make_request(SSS_NSS_ENDPWENT, NULL, NULL, NULL, &errnop); if (nret != NSS_STATUS_SUCCESS) { errno = errnop; return nret; } return NSS_STATUS_SUCCESS; }