Added support for user groups in lxc-usernet

Signed-off-by: Henrik Kjölhede <hkjolhede@gmail.com>
This commit is contained in:
Henrik Kjölhede 2015-05-11 21:11:04 +02:00
parent 54c23a6a62
commit 3043883c5a

View File

@ -23,6 +23,7 @@
#include <stdbool.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
@ -96,6 +97,116 @@ static char *get_username(void)
return pwd->pw_name;
}
static char **get_groupnames(void)
{
int ngroups;
gid_t *group_ids;
int ret, i, j;
char **group_names;
struct group *gr;
ngroups = getgroups(0, NULL);
group_ids = (gid_t *)malloc(sizeof(gid_t)*ngroups);
ret = getgroups(ngroups, group_ids);
if (ret < 0) {
free(group_ids);
fprintf(stderr, "Failed to get process groups\n");
return NULL;
}
group_names = (char **)malloc(sizeof(char *)*(ngroups+1));
for (i=0; i<ngroups; i++ ) {
gr = getgrgid(group_ids[i]);
if (gr == NULL) {
fprintf(stderr, "Failed to get group name\n");
free(group_ids);
for (j=0; j<i; j++) {
free(group_names[j]);
}
free(group_names);
return NULL;
}
group_names[i] = strdup(gr->gr_name);
if (group_names[i] == NULL) {
fprintf(stderr, "Failed to copy group name: %s", gr->gr_name);
free(group_ids);
for (j=0; j<i; j++) {
free(group_names[j]);
}
free(group_names);
return NULL;
}
}
group_names[ngroups] = NULL;
free(group_ids);
return group_names;
}
struct alloted_s {
char *name;
int allowed;
struct alloted_s *next;
};
static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int n)
{
struct alloted_s *cur, *al;
if (head == NULL || name == NULL) {
// sanity check. parameters should not be null
return NULL;
}
al = (struct alloted_s *)malloc(sizeof(struct alloted_s));
if (al == NULL) {
// unable to allocate memory to new struct
return NULL;
}
al->name = strdup(name);
al->allowed = n;
al->next = NULL;
if (*head == NULL) {
*head = al;
return al;
}
cur = *head;
while (cur->next != NULL)
cur = cur->next;
cur->next = al;
return al;
}
static void free_alloted(struct alloted_s **head)
{
struct alloted_s *cur;
if (head == NULL) {
return;
}
cur = *head;
while (cur != NULL) {
cur = cur->next;
free((*head)->name);
free(*head);
*head = cur;
}
}
/* The configuration file consists of lines of the form:
*
* user type bridge count
@ -103,13 +214,15 @@ static char *get_username(void)
* Return the count entry for the calling user if there is one. Else
* return -1.
*/
static int get_alloted(char *me, char *intype, char *link)
static int get_alloted(char *me, char *intype, char *link, struct alloted_s **alloted)
{
FILE *fin = fopen(LXC_USERNIC_CONF, "r");
char *line = NULL;
char user[100], type[100], br[100];
size_t len = 0;
int n = -1, ret;
int n, ret, count = 0;
char **groups;
char **g;
if (!fin) {
fprintf(stderr, "Failed to open %s: %s\n", LXC_USERNIC_CONF,
@ -128,13 +241,48 @@ static int get_alloted(char *me, char *intype, char *link)
continue;
if (strcmp(link, br) != 0)
continue;
free(line);
fclose(fin);
return n;
append_alloted(alloted, me, n);
count += n;
break; // found the user with the appropriate settings, therefore finish the search.
// what to do if there are more than one applicable lines? not specified in the docs.
// since getline is implemented with realloc, we don't need to free line until exiting func.
}
// now parse any possible groups specified
groups = get_groupnames();
if (groups != NULL) {
fseek(fin, 0, SEEK_SET);
while ((getline(&line, &len, fin)) != -1) {
ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", user, type, br, &n);
if (ret != 4)
continue;
if (user[0] != '@')
continue;
if (strcmp(type, intype) != 0)
continue;
if (strcmp(link, br) != 0)
continue;
for (g=groups; *g!=NULL; g++) {
if (strcmp(user+1, *g) == 0) {
append_alloted(alloted, user, n);
count += n;
break;
}
}
}
for (g=groups; *g!=NULL; g++)
free(*g);
free(groups);
}
fclose(fin);
free(line);
return -1;
return count;
}
static char *get_eol(char *s, char *e)
@ -361,7 +509,7 @@ static bool cull_entries(int fd, char *me, char *t, char *br)
p += entry_lines[n-1].len + 1;
if (p >= e)
break;
}
}
p = buf;
for (i=0; i<n; i++) {
if (!entry_lines[i].keep)
@ -396,18 +544,23 @@ static int count_entries(char *buf, off_t len, char *me, char *t, char *br)
* The dbfile has lines of the format:
* user type bridge nicname
*/
static bool get_nic_if_avail(int fd, char *me, int pid, char *intype, char *br, int allowed, char **nicname, char **cnic)
static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid, char *intype, char *br, int allowed, char **nicname, char **cnic)
{
off_t len, slen;
struct stat sb;
char *buf = NULL, *newline;
int ret, count = 0;
char *owner;
struct alloted_s *n;
cull_entries(fd, me, intype, br);
for (n=names; n!=NULL; n=n->next)
cull_entries(fd, n->name, intype, br);
if (allowed == 0)
return false;
owner = names->name;
if (fstat(fd, &sb) < 0) {
fprintf(stderr, "Failed to fstat: %s\n", strerror(errno));
return false;
@ -420,17 +573,27 @@ static bool get_nic_if_avail(int fd, char *me, int pid, char *intype, char *br,
return false;
}
count = count_entries(buf, len, me, intype, br);
if (count >= allowed)
return false;
owner = NULL;
for (n=names; n!=NULL; n=n->next) {
count = count_entries(buf, len, n->name, intype, br);
if (count >= n->allowed)
continue;
owner = n->name;
break;
}
}
if (owner == NULL)
return false;
if (!get_new_nicname(nicname, br, pid, cnic))
return false;
/* me ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
slen = strlen(me) + strlen(intype) + strlen(br) + strlen(*nicname) + 5;
/* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(*nicname) + 5;
newline = alloca(slen);
ret = snprintf(newline, slen, "%s %s %s %s\n", me, intype, br, *nicname);
ret = snprintf(newline, slen, "%s %s %s %s\n", owner, intype, br, *nicname);
if (ret < 0 || ret >= slen) {
if (lxc_netdev_delete_by_name(*nicname) != 0)
fprintf(stderr, "Error unlinking %s!\n", *nicname);
@ -592,6 +755,7 @@ int main(int argc, char *argv[])
char *cnic = NULL; // created nic name in container is returned here.
char *vethname = NULL;
int pid;
struct alloted_s *alloted = NULL;
/* set a sane env, because we are setuid-root */
if (clearenv() < 0) {
@ -631,14 +795,16 @@ int main(int argc, char *argv[])
if (!may_access_netns(pid)) {
fprintf(stderr, "User %s may not modify netns for pid %d\n",
me, pid);
me, pid);
exit(1);
}
n = get_alloted(me, argv[2], argv[3]);
n = get_alloted(me, argv[2], argv[3], &alloted);
if (n > 0)
gotone = get_nic_if_avail(fd, me, pid, argv[2], argv[3], n, &nicname, &cnic);
gotone = get_nic_if_avail(fd, alloted, pid, argv[2], argv[3], n, &nicname, &cnic);
close(fd);
free_alloted(&alloted);
if (!gotone) {
fprintf(stderr, "Quota reached\n");
exit(1);