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 <stdbool.h>
#include <sys/types.h> #include <sys/types.h>
#include <pwd.h> #include <pwd.h>
#include <grp.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/file.h> #include <sys/file.h>
@ -96,6 +97,116 @@ static char *get_username(void)
return pwd->pw_name; 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: /* The configuration file consists of lines of the form:
* *
* user type bridge count * 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 the count entry for the calling user if there is one. Else
* return -1. * 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"); FILE *fin = fopen(LXC_USERNIC_CONF, "r");
char *line = NULL; char *line = NULL;
char user[100], type[100], br[100]; char user[100], type[100], br[100];
size_t len = 0; size_t len = 0;
int n = -1, ret; int n, ret, count = 0;
char **groups;
char **g;
if (!fin) { if (!fin) {
fprintf(stderr, "Failed to open %s: %s\n", LXC_USERNIC_CONF, 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; continue;
if (strcmp(link, br) != 0) if (strcmp(link, br) != 0)
continue; continue;
free(line);
fclose(fin); append_alloted(alloted, me, n);
return 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); fclose(fin);
free(line); free(line);
return -1; return count;
} }
static char *get_eol(char *s, char *e) 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; p += entry_lines[n-1].len + 1;
if (p >= e) if (p >= e)
break; break;
} }
p = buf; p = buf;
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
if (!entry_lines[i].keep) 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: * The dbfile has lines of the format:
* user type bridge nicname * 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; off_t len, slen;
struct stat sb; struct stat sb;
char *buf = NULL, *newline; char *buf = NULL, *newline;
int ret, count = 0; 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) if (allowed == 0)
return false; return false;
owner = names->name;
if (fstat(fd, &sb) < 0) { if (fstat(fd, &sb) < 0) {
fprintf(stderr, "Failed to fstat: %s\n", strerror(errno)); fprintf(stderr, "Failed to fstat: %s\n", strerror(errno));
return false; return false;
@ -420,17 +573,27 @@ static bool get_nic_if_avail(int fd, char *me, int pid, char *intype, char *br,
return false; return false;
} }
count = count_entries(buf, len, me, intype, br); owner = NULL;
if (count >= allowed) for (n=names; n!=NULL; n=n->next) {
return false; 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)) if (!get_new_nicname(nicname, br, pid, cnic))
return false; return false;
/* me ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */ /* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
slen = strlen(me) + strlen(intype) + strlen(br) + strlen(*nicname) + 5; slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(*nicname) + 5;
newline = alloca(slen); 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 (ret < 0 || ret >= slen) {
if (lxc_netdev_delete_by_name(*nicname) != 0) if (lxc_netdev_delete_by_name(*nicname) != 0)
fprintf(stderr, "Error unlinking %s!\n", *nicname); 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 *cnic = NULL; // created nic name in container is returned here.
char *vethname = NULL; char *vethname = NULL;
int pid; int pid;
struct alloted_s *alloted = NULL;
/* set a sane env, because we are setuid-root */ /* set a sane env, because we are setuid-root */
if (clearenv() < 0) { if (clearenv() < 0) {
@ -631,14 +795,16 @@ int main(int argc, char *argv[])
if (!may_access_netns(pid)) { if (!may_access_netns(pid)) {
fprintf(stderr, "User %s may not modify netns for pid %d\n", fprintf(stderr, "User %s may not modify netns for pid %d\n",
me, pid); me, pid);
exit(1); exit(1);
} }
n = get_alloted(me, argv[2], argv[3]); n = get_alloted(me, argv[2], argv[3], &alloted);
if (n > 0) 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); close(fd);
free_alloted(&alloted);
if (!gotone) { if (!gotone) {
fprintf(stderr, "Quota reached\n"); fprintf(stderr, "Quota reached\n");
exit(1); exit(1);