enum nss_status _nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); char *tmpbuf; enum nss_status status; ent_t intern = { TRUE, NULL, {NULL, 0, 0} }; status = internal_setgrent (&intern); if (status != NSS_STATUS_SUCCESS) return status; tmpbuf = __alloca (buflen); do { while ((status = internal_getgrent_r (&intern, tmpbuf, buflen, user, group, start, size, groupsp, limit, errnop)) == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen); } while (status == NSS_STATUS_SUCCESS); internal_endgrent (&intern); return NSS_STATUS_SUCCESS; }
static int get_uid (const char *user, uid_t *uidp) { size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); char *buf = (char *) alloca (buflen); while (1) { struct passwd result; struct passwd *resp; int r = getpwnam_r (user, &result, buf, buflen, &resp); if (r == 0 && resp != NULL) { *uidp = resp->pw_uid; return 0; } if (r != ERANGE) break; buf = extend_alloca (buf, buflen, 2 * buflen); } return 1; }
int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *cpuset) { size_t cnt; if (unlikely (__kernel_cpumask_size == 0)) { INTERNAL_SYSCALL_DECL (err); int res; size_t psize = 128; void *p = alloca (psize); while (res = INTERNAL_SYSCALL (sched_getaffinity, err, 3, getpid (), psize, p), INTERNAL_SYSCALL_ERROR_P (res, err) && INTERNAL_SYSCALL_ERRNO (res, err) == EINVAL) p = extend_alloca (p, psize, 2 * psize); if (res == 0 || INTERNAL_SYSCALL_ERROR_P (res, err)) { __set_errno (INTERNAL_SYSCALL_ERRNO (res, err)); return -1; } __kernel_cpumask_size = res; } /* We now know the size of the kernel cpumask_t. Make sure the user does not request to set a bit beyond that. */ for (cnt = __kernel_cpumask_size; cnt < cpusetsize; ++cnt) if (((char *) cpuset)[cnt] != '\0') { /* Found a nonzero byte. This means the user request cannot be fulfilled. */ __set_errno (EINVAL); return -1; } return INLINE_SYSCALL (sched_setaffinity, 3, pid, cpusetsize, cpuset); }
enum nss_status _nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); char *tmpbuf; enum nss_status status; ent_t intern = { true, false, false, NULL, {NULL, 0, 0} }; bool use_malloc = false; status = internal_setgrent (&intern); if (status != NSS_STATUS_SUCCESS) return status; tmpbuf = __alloca (buflen); do { while ((status = internal_getgrent_r (&intern, tmpbuf, buflen, user, group, start, size, groupsp, limit, errnop)) == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) if (__libc_use_alloca (buflen * 2)) tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen); else { buflen *= 2; char *newbuf = realloc (use_malloc ? tmpbuf : NULL, buflen); if (newbuf == NULL) { status = NSS_STATUS_TRYAGAIN; goto done; } use_malloc = true; tmpbuf = newbuf; } } while (status == NSS_STATUS_SUCCESS); status = NSS_STATUS_SUCCESS; done: if (use_malloc) free (tmpbuf); internal_endgrent (&intern); return status; }
int __sched_setaffinity_new (pid_t pid, size_t cpusetsize, const cpu_set_t *cpuset) { if (__builtin_expect (__kernel_cpumask_size == 0, 0)) { INTERNAL_SYSCALL_DECL (err); int res; size_t psize = 128; void *p = alloca (psize); while (res = INTERNAL_SYSCALL (sched_getaffinity, err, 3, getpid (), psize, p), INTERNAL_SYSCALL_ERROR_P (res, err) && INTERNAL_SYSCALL_ERRNO (res, err) == EINVAL) p = extend_alloca (p, psize, 2 * psize); if (res == 0 || INTERNAL_SYSCALL_ERROR_P (res, err)) { __set_errno (INTERNAL_SYSCALL_ERRNO (res, err)); return -1; } __kernel_cpumask_size = res; } /* We now know the size of the kernel cpumask_t. Make sure the user does not request to set a bit beyond that. */ for (size_t cnt = __kernel_cpumask_size; cnt < cpusetsize; ++cnt) if (((char *) cpuset)[cnt] != '\0') { /* Found a nonzero byte. This means the user request cannot be fulfilled. */ __set_errno (EINVAL); return -1; } int result = INLINE_SYSCALL (sched_setaffinity, 3, pid, cpusetsize, cpuset); #ifdef RESET_VGETCPU_CACHE if (result != -1) RESET_VGETCPU_CACHE (); #endif return result; }
int __determine_cpumask_size (pid_t tid) { INTERNAL_SYSCALL_DECL (err); int res; size_t psize = 128; void *p = alloca (psize); while (res = INTERNAL_SYSCALL (sched_getaffinity, err, 3, tid, psize, p), INTERNAL_SYSCALL_ERROR_P (res, err) && INTERNAL_SYSCALL_ERRNO (res, err) == EINVAL) p = extend_alloca (p, psize, 2 * psize); if (res == 0 || INTERNAL_SYSCALL_ERROR_P (res, err)) return INTERNAL_SYSCALL_ERRNO (res, err); __kernel_cpumask_size = res; return 0; }
enum nss_status _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { /* We always need the domain name. */ char *domainname; if (yp_get_default_domain (&domainname)) return NSS_STATUS_UNAVAIL; /* Check whether we are supposed to use the netid.byname map. */ if (_nsl_default_nss () & NSS_FLAG_NETID_AUTHORITATIVE) { /* We need the user ID. */ uid_t uid; if (get_uid (user, &uid) == 0 && initgroups_netid (uid, group, start, size, groupsp, limit, errnop, domainname) == NSS_STATUS_SUCCESS) return NSS_STATUS_SUCCESS; } struct group grpbuf, *g; size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); char *tmpbuf; enum nss_status status; intern_t intern = { NULL, NULL, 0 }; gid_t *groups = *groupsp; status = internal_setgrent (domainname, &intern); if (status != NSS_STATUS_SUCCESS) return status; tmpbuf = __alloca (buflen); while (1) { while ((status = internal_getgrent_r (&grpbuf, tmpbuf, buflen, errnop, &intern)) == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen); if (status != NSS_STATUS_SUCCESS) { if (status == NSS_STATUS_NOTFOUND) status = NSS_STATUS_SUCCESS; goto done; } g = &grpbuf; if (g->gr_gid != group) { char **m; for (m = g->gr_mem; *m != NULL; ++m) if (strcmp (*m, user) == 0) { /* Matches user. Insert this group. */ if (*start == *size) { /* Need a bigger buffer. */ gid_t *newgroups; long int newsize; if (limit > 0 && *size == limit) /* We reached the maximum. */ goto done; if (limit <= 0) newsize = 2 * *size; else newsize = MIN (limit, 2 * *size); newgroups = realloc (groups, newsize * sizeof (*groups)); if (newgroups == NULL) { status = NSS_STATUS_TRYAGAIN; *errnop = errno; goto done; } *groupsp = groups = newgroups; *size = newsize; } groups[*start] = g->gr_gid; *start += 1; break; } } } done: while (intern.start != NULL) { intern.next = intern.start; intern.start = intern.start->next; free (intern.next); } return status; }
/* Get the next group from NSS (+ entry). If the NSS module supports initgroups_dyn, get all entries at once. */ static enum nss_status getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { enum nss_status status; struct group grpbuf; /* if this module does not support getgrent_r and initgroups_dyn, abort. We cannot find the needed group entries. */ if (nss_getgrent_r == NULL && nss_initgroups_dyn == NULL) return NSS_STATUS_UNAVAIL; /* Try nss_initgroups_dyn if supported. We also need getgrgid_r. If this function is not supported, step through the whole group database with getgrent_r. */ if (nss_initgroups_dyn && nss_getgrgid_r) { long int mystart = 0; long int mysize = limit <= 0 ? *size : limit; gid_t *mygroups = malloc (mysize * sizeof (gid_t)); if (mygroups == NULL) return NSS_STATUS_TRYAGAIN; /* For every gid in the list we get from the NSS module, get the whole group entry. We need to do this, since we need the group name to check if it is in the blacklist. In worst case, this is as twice as slow as stepping with getgrent_r through the whole group database. But for large group databases this is faster, since the user can only be in a limited number of groups. */ if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups, limit, errnop) == NSS_STATUS_SUCCESS) { /* A temporary buffer. We use the normal buffer, until we find an entry, for which this buffer is to small. In this case, we overwrite the pointer with one to a bigger buffer. */ char *tmpbuf = buffer; size_t tmplen = buflen; int i; for (i = 0; i < mystart; i++) { while ((status = nss_getgrgid_r (mygroups[i], &grpbuf, tmpbuf, tmplen, errnop)) == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) if (tmpbuf == buffer) { tmplen *= 2; tmpbuf = __alloca (tmplen); } else tmpbuf = extend_alloca (tmpbuf, tmplen, 2 * tmplen); if (!in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent)) check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); } free (mygroups); return NSS_STATUS_NOTFOUND; } free (mygroups); } /* If we come here, the NSS module does not support initgroups_dyn and we have to step through the whole list ourself. */ do { if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) != NSS_STATUS_SUCCESS) return status; } while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent)); check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); return NSS_STATUS_SUCCESS; }
int __netlink_request (struct netlink_handle *h, int type) { struct netlink_res *nlm_next; struct netlink_res **new_nlm_list; static volatile size_t buf_size = 4096; char *buf; struct sockaddr_nl nladdr; struct nlmsghdr *nlmh; ssize_t read_len; bool done = false; bool use_malloc = false; if (__netlink_sendreq (h, type) < 0) return -1; size_t this_buf_size = buf_size; if (__libc_use_alloca (this_buf_size)) buf = alloca (this_buf_size); else { buf = malloc (this_buf_size); if (buf != NULL) use_malloc = true; else goto out_fail; } struct iovec iov = { buf, this_buf_size }; if (h->nlm_list != NULL) new_nlm_list = &h->end_ptr->next; else new_nlm_list = &h->nlm_list; while (! done) { struct msghdr msg = { (void *) &nladdr, sizeof (nladdr), &iov, 1, NULL, 0, 0 }; read_len = TEMP_FAILURE_RETRY (__recvmsg (h->fd, &msg, 0)); if (read_len < 0) goto out_fail; if (nladdr.nl_pid != 0) continue; if (__builtin_expect (msg.msg_flags & MSG_TRUNC, 0)) { if (this_buf_size >= SIZE_MAX / 2) goto out_fail; nlm_next = *new_nlm_list; while (nlm_next != NULL) { struct netlink_res *tmpptr; tmpptr = nlm_next->next; free (nlm_next); nlm_next = tmpptr; } *new_nlm_list = NULL; if (__libc_use_alloca (2 * this_buf_size)) buf = extend_alloca (buf, this_buf_size, 2 * this_buf_size); else { this_buf_size *= 2; char *new_buf = realloc (use_malloc ? buf : NULL, this_buf_size); if (new_buf == NULL) goto out_fail; new_buf = buf; use_malloc = true; } buf_size = this_buf_size; iov.iov_base = buf; iov.iov_len = this_buf_size; /* Increase sequence number, so that we can distinguish between old and new request messages. */ h->seq++; if (__netlink_sendreq (h, type) < 0) goto out_fail; continue; } size_t count = 0; size_t remaining_len = read_len; for (nlmh = (struct nlmsghdr *) buf; NLMSG_OK (nlmh, remaining_len); nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, remaining_len)) { if ((pid_t) nlmh->nlmsg_pid != h->pid || nlmh->nlmsg_seq != h->seq) continue; ++count; if (nlmh->nlmsg_type == NLMSG_DONE) { /* We found the end, leave the loop. */ done = true; break; } if (nlmh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nlmh); if (nlmh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) errno = EIO; else errno = -nlerr->error; goto out_fail; } } /* If there was nothing with the expected nlmsg_pid and nlmsg_seq, there is no point to record it. */ if (count == 0) continue; nlm_next = (struct netlink_res *) malloc (sizeof (struct netlink_res) + read_len); if (nlm_next == NULL) goto out_fail; nlm_next->next = NULL; nlm_next->nlh = memcpy (nlm_next + 1, buf, read_len); nlm_next->size = read_len; nlm_next->seq = h->seq; if (h->nlm_list == NULL) h->nlm_list = nlm_next; else h->end_ptr->next = nlm_next; h->end_ptr = nlm_next; } if (use_malloc) free (buf); return 0; out_fail: if (use_malloc) free (buf); return -1; }
/* Get the next group from NSS (+ entry). If the NSS module supports initgroups_dyn, get all entries at once. */ static enum nss_status getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { enum nss_status status; struct group grpbuf; /* Try nss_initgroups_dyn if supported. We also need getgrgid_r. If this function is not supported, step through the whole group database with getgrent_r. */ if (! ent->skip_initgroups_dyn) { long int mystart = 0; long int mysize = limit <= 0 ? *size : limit; gid_t *mygroups = malloc (mysize * sizeof (gid_t)); if (mygroups == NULL) return NSS_STATUS_TRYAGAIN; /* For every gid in the list we get from the NSS module, get the whole group entry. We need to do this, since we need the group name to check if it is in the blacklist. In worst case, this is as twice as slow as stepping with getgrent_r through the whole group database. But for large group databases this is faster, since the user can only be in a limited number of groups. */ if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups, limit, errnop) == NSS_STATUS_SUCCESS) { status = NSS_STATUS_NOTFOUND; /* If there is no blacklist we can trust the underlying initgroups implementation. */ if (ent->blacklist.current <= 1) for (int i = 0; i < mystart; i++) add_group (start, size, groupsp, limit, mygroups[i]); else { /* A temporary buffer. We use the normal buffer, until we find an entry, for which this buffer is to small. In this case, we overwrite the pointer with one to a bigger buffer. */ char *tmpbuf = buffer; size_t tmplen = buflen; bool use_malloc = false; for (int i = 0; i < mystart; i++) { while ((status = nss_getgrgid_r (mygroups[i], &grpbuf, tmpbuf, tmplen, errnop)) == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) { if (__libc_use_alloca (tmplen * 2)) { if (tmpbuf == buffer) { tmplen *= 2; tmpbuf = __alloca (tmplen); } else tmpbuf = extend_alloca (tmpbuf, tmplen, tmplen * 2); } else { tmplen *= 2; char *newbuf = realloc (use_malloc ? tmpbuf : NULL, tmplen); if (newbuf == NULL) { status = NSS_STATUS_TRYAGAIN; goto done; } use_malloc = true; tmpbuf = newbuf; } } if (__builtin_expect (status != NSS_STATUS_NOTFOUND, 1)) { if (__builtin_expect (status != NSS_STATUS_SUCCESS, 0)) goto done; if (!in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent) && check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf)) { if (nss_setgrent != NULL) { nss_setgrent (1); ent->need_endgrent = true; } ent->skip_initgroups_dyn = true; goto iter; } } } status = NSS_STATUS_NOTFOUND; done: if (use_malloc) free (tmpbuf); } free (mygroups); return status; } free (mygroups); } /* If we come here, the NSS module does not support initgroups_dyn or we were confronted with a split group. In these cases we have to step through the whole list ourself. */ iter: do { if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) != NSS_STATUS_SUCCESS) break; } while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent)); if (status == NSS_STATUS_SUCCESS) check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); return status; }
static struct if_nameindex * if_nameindex_ioctl (void) { int fd = __opensock (); struct ifconf ifc; unsigned int nifs, i; int rq_len; struct if_nameindex *idx = NULL; # define RQ_IFS 4 if (fd < 0) return NULL; ifc.ifc_buf = NULL; /* We may be able to get the needed buffer size directly, rather than guessing. */ if (! old_siocgifconf) { ifc.ifc_buf = NULL; ifc.ifc_len = 0; if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0 || ifc.ifc_len == 0) { # if __ASSUME_SIOCGIFNAME == 0 old_siocgifconf = 1; # endif rq_len = RQ_IFS * sizeof (struct ifreq); } else rq_len = ifc.ifc_len; } else rq_len = RQ_IFS * sizeof (struct ifreq); /* Read all the interfaces out of the kernel. */ ifc.ifc_buf = alloca (rq_len); ifc.ifc_len = rq_len; while (1) { if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0) { close_not_cancel_no_status (fd); return NULL; } if (ifc.ifc_len < rq_len || ! old_siocgifconf) break; ifc.ifc_buf = extend_alloca (ifc.ifc_buf, rq_len, 2 * rq_len); ifc.ifc_len = rq_len; } nifs = ifc.ifc_len / sizeof (struct ifreq); idx = malloc ((nifs + 1) * sizeof (struct if_nameindex)); if (idx == NULL) { close_not_cancel_no_status (fd); __set_errno (ENOBUFS); return NULL; } for (i = 0; i < nifs; ++i) { struct ifreq *ifr = &ifc.ifc_req[i]; idx[i].if_name = __strdup (ifr->ifr_name); if (idx[i].if_name == NULL || __ioctl (fd, SIOCGIFINDEX, ifr) < 0) { int saved_errno = errno; unsigned int j; for (j = 0; j < i; ++j) free (idx[j].if_name); free (idx); close_not_cancel_no_status (fd); if (saved_errno == EINVAL) saved_errno = ENOSYS; else if (saved_errno == ENOMEM) saved_errno = ENOBUFS; __set_errno (saved_errno); return NULL; } idx[i].if_index = ifr->ifr_ifindex; } idx[i].if_index = 0; idx[i].if_name = NULL; close_not_cancel_no_status (fd); return idx; }
enum nss_status _nss_files_initgroups_dyn (const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { FILE *stream = fopen ("/etc/group", "rce"); if (stream == NULL) { *errnop = errno; return *errnop == ENOMEM ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; } /* No other thread using this stream. */ __fsetlocking (stream, FSETLOCKING_BYCALLER); char *line = NULL; size_t linelen = 0; enum nss_status status = NSS_STATUS_SUCCESS; bool any = false; size_t buflen = 1024; void *buffer = alloca (buflen); bool buffer_use_malloc = false; gid_t *groups = *groupsp; /* We have to iterate over the entire file. */ while (1) { fpos_t pos; fgetpos (stream, &pos); ssize_t n = getline (&line, &linelen, stream); if (n < 0) { if (! feof_unlocked (stream)) status = ((*errnop = errno) == ENOMEM ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL); break; } struct group grp; int res = _nss_files_parse_grent (line, &grp, buffer, buflen, errnop); if (res == -1) { size_t newbuflen = 2 * buflen; if (buffer_use_malloc || ! __libc_use_alloca (buflen + newbuflen)) { void *newbuf = realloc (buffer_use_malloc ? buffer : NULL, newbuflen); if (newbuf == NULL) { *errnop = ENOMEM; status = NSS_STATUS_TRYAGAIN; goto out; } buffer = newbuf; buflen = newbuflen; buffer_use_malloc = true; } else buffer = extend_alloca (buffer, buflen, newbuflen); /* Reread current line, the parser has clobbered it. */ fsetpos (stream, &pos); continue; } if (res > 0 && grp.gr_gid != group) for (char **m = grp.gr_mem; *m != NULL; ++m) if (strcmp (*m, user) == 0) { /* Matches user. Insert this group. */ if (*start == *size) { /* Need a bigger buffer. */ if (limit > 0 && *size == limit) /* We reached the maximum. */ goto out; long int newsize; if (limit <= 0) newsize = 2 * *size; else newsize = MIN (limit, 2 * *size); gid_t *newgroups = realloc (groups, newsize * sizeof (*groups)); if (newgroups == NULL) { *errnop = ENOMEM; status = NSS_STATUS_TRYAGAIN; goto out; } *groupsp = groups = newgroups; *size = newsize; } groups[*start] = grp.gr_gid; *start += 1; any = true; break; } } out: /* Free memory. */ if (buffer_use_malloc) free (buffer); free (line); fclose (stream); return status == NSS_STATUS_SUCCESS && !any ? NSS_STATUS_NOTFOUND : status; }