Skip to content

Commit

Permalink
Added support for user groups in lxc-usernet
Browse files Browse the repository at this point in the history
Signed-off-by: Henrik Kjölhede <hkjolhede@gmail.com>
  • Loading branch information
hkjolhede committed May 11, 2015
1 parent 54c23a6 commit 3043883
Showing 1 changed file with 184 additions and 18 deletions.
202 changes: 184 additions & 18 deletions src/lxc/lxc_user_nic.c
Expand Up @@ -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>
Expand Down Expand Up @@ -96,20 +97,132 @@ 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
*
* 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,
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 3043883

Please sign in to comment.