Unverified Commit f6c0d0b3 by Christian Brauner Committed by Stéphane Graber

lxc-user-nic: improve + bugfix

parent 4350bfc4
...@@ -73,9 +73,8 @@ static int open_and_lock(char *path) ...@@ -73,9 +73,8 @@ static int open_and_lock(char *path)
fd = open(path, O_RDWR|O_CREAT, S_IWUSR | S_IRUSR); fd = open(path, O_RDWR|O_CREAT, S_IWUSR | S_IRUSR);
if (fd < 0) { if (fd < 0) {
fprintf(stderr, "Failed to open %s: %s\n", usernic_error("Failed to open %s: %s.\n", path, strerror(errno));
path, strerror(errno)); return -1;
return(fd);
} }
lk.l_type = F_WRLCK; lk.l_type = F_WRLCK;
...@@ -83,8 +82,7 @@ static int open_and_lock(char *path) ...@@ -83,8 +82,7 @@ static int open_and_lock(char *path)
lk.l_start = 0; lk.l_start = 0;
lk.l_len = 0; lk.l_len = 0;
if (fcntl(fd, F_SETLKW, &lk) < 0) { if (fcntl(fd, F_SETLKW, &lk) < 0) {
fprintf(stderr, "Failed to lock %s: %s\n", usernic_error("Failed to lock %s: %s.\n", path, strerror(errno));
path, strerror(errno));
close(fd); close(fd);
return -1; return -1;
} }
...@@ -95,10 +93,11 @@ static int open_and_lock(char *path) ...@@ -95,10 +93,11 @@ static int open_and_lock(char *path)
static char *get_username(void) static char *get_username(void)
{ {
struct passwd *pwd = getpwuid(getuid()); struct passwd *pwd;
if (pwd == NULL) { pwd = getpwuid(getuid());
perror("getpwuid"); if (!pwd) {
usernic_error("Failed to call get username: %s.\n", strerror(errno));
return NULL; return NULL;
} }
...@@ -108,10 +107,13 @@ static char *get_username(void) ...@@ -108,10 +107,13 @@ static char *get_username(void)
static void free_groupnames(char **groupnames) static void free_groupnames(char **groupnames)
{ {
int i; int i;
if (!groupnames) if (!groupnames)
return; return;
for (i = 0; groupnames[i]; i++) for (i = 0; groupnames[i]; i++)
free(groupnames[i]); free(groupnames[i]);
free(groupnames); free(groupnames);
} }
...@@ -124,53 +126,56 @@ static char **get_groupnames(void) ...@@ -124,53 +126,56 @@ static char **get_groupnames(void)
struct group *gr; struct group *gr;
ngroups = getgroups(0, NULL); ngroups = getgroups(0, NULL);
if (ngroups < 0) {
if (ngroups == -1) { usernic_error(
fprintf(stderr, "Failed to get number of groups user belongs to: %s\n", strerror(errno)); "Failed to get number of groups the user belongs to: %s.\n",
strerror(errno));
return NULL; return NULL;
} }
if (ngroups == 0) if (ngroups == 0)
return NULL; return NULL;
group_ids = (gid_t *)malloc(sizeof(gid_t)*ngroups); group_ids = malloc(sizeof(gid_t) * ngroups);
if (!group_ids) {
if (group_ids == NULL) { usernic_error("Failed to allocate memory while getting groups "
fprintf(stderr, "Out of memory while getting groups the user belongs to\n"); "the user belongs to: %s.\n",
strerror(errno));
return NULL; return NULL;
} }
ret = getgroups(ngroups, group_ids); ret = getgroups(ngroups, group_ids);
if (ret < 0) { if (ret < 0) {
free(group_ids); free(group_ids);
fprintf(stderr, "Failed to get process groups: %s\n", strerror(errno)); usernic_error("Failed to get process groups: %s.\n",
strerror(errno));
return NULL; return NULL;
} }
groupnames = (char **)malloc(sizeof(char *)*(ngroups+1)); groupnames = malloc(sizeof(char *) * (ngroups + 1));
if (!groupnames) {
if (groupnames == NULL) {
free(group_ids); free(group_ids);
fprintf(stderr, "Out of memory while getting group names\n"); usernic_error("Failed to allocate memory while getting group "
"names: %s.\n",
strerror(errno));
return NULL; return NULL;
} }
memset(groupnames, 0, sizeof(char *)*(ngroups+1)); memset(groupnames, 0, sizeof(char *) * (ngroups + 1));
for (i=0; i<ngroups; i++ ) { for (i = 0; i < ngroups; i++) {
gr = getgrgid(group_ids[i]); gr = getgrgid(group_ids[i]);
if (!gr) {
if (gr == NULL) { usernic_error("Failed to get group name: %s.\n",
fprintf(stderr, "Failed to get group name\n"); strerror(errno));
free(group_ids); free(group_ids);
free_groupnames(groupnames); free_groupnames(groupnames);
return NULL; return NULL;
} }
groupnames[i] = strdup(gr->gr_name); groupnames[i] = strdup(gr->gr_name);
if (!groupnames[i]) {
if (groupnames[i] == NULL) { usernic_error("Failed to copy group name \"%s\".",
fprintf(stderr, "Failed to copy group name: %s", gr->gr_name); gr->gr_name);
free(group_ids); free(group_ids);
free_groupnames(groupnames); free_groupnames(groupnames);
return NULL; return NULL;
...@@ -184,8 +189,8 @@ static char **get_groupnames(void) ...@@ -184,8 +189,8 @@ static char **get_groupnames(void)
static bool name_is_in_groupnames(char *name, char **groupnames) static bool name_is_in_groupnames(char *name, char **groupnames)
{ {
while (groupnames != NULL) { while (groupnames) {
if (strcmp(name, *groupnames) == 0) if (!strcmp(name, *groupnames))
return true; return true;
groupnames++; groupnames++;
} }
...@@ -202,23 +207,20 @@ static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int ...@@ -202,23 +207,20 @@ static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int
{ {
struct alloted_s *cur, *al; struct alloted_s *cur, *al;
if (head == NULL || name == NULL) { if (!head || !name) {
// sanity check. parameters should not be null // sanity check. parameters should not be null
fprintf(stderr, "NULL parameters to append_alloted not allowed\n"); usernic_error("%s\n", "Unexpected NULL argument.");
return NULL; return NULL;
} }
al = (struct alloted_s *)malloc(sizeof(struct alloted_s)); al = malloc(sizeof(struct alloted_s));
if (!al) {
if (al == NULL) { usernic_error("Failed to allocate memory: %s.\n", strerror(errno));
// unable to allocate memory to new struct
fprintf(stderr, "Out of memory in append_alloted\n");
return NULL; return NULL;
} }
al->name = strdup(name); al->name = strdup(name);
if (!al->name) {
if (al->name == NULL) {
free(al); free(al);
return NULL; return NULL;
} }
...@@ -226,16 +228,16 @@ static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int ...@@ -226,16 +228,16 @@ static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int
al->allowed = n; al->allowed = n;
al->next = NULL; al->next = NULL;
if (*head == NULL) { if (!*head) {
*head = al; *head = al;
return al; return al;
} }
cur = *head; cur = *head;
while (cur->next != NULL) while (cur->next)
cur = cur->next; cur = cur->next;
cur->next = al; cur->next = al;
return al; return al;
} }
...@@ -243,13 +245,11 @@ static void free_alloted(struct alloted_s **head) ...@@ -243,13 +245,11 @@ static void free_alloted(struct alloted_s **head)
{ {
struct alloted_s *cur; struct alloted_s *cur;
if (head == NULL) { if (!head)
return; return;
}
cur = *head; cur = *head;
while (cur) {
while (cur != NULL) {
cur = cur->next; cur = cur->next;
free((*head)->name); free((*head)->name);
free(*head); free(*head);
...@@ -268,49 +268,55 @@ static void free_alloted(struct alloted_s **head) ...@@ -268,49 +268,55 @@ static void free_alloted(struct alloted_s **head)
*/ */
static int get_alloted(char *me, char *intype, char *link, struct alloted_s **alloted) static int get_alloted(char *me, char *intype, char *link, struct alloted_s **alloted)
{ {
FILE *fin = fopen(LXC_USERNIC_CONF, "r"); int n, ret;
char *line = NULL;
char name[100], type[100], br[100]; char name[100], type[100], br[100];
size_t len = 0;
int n, ret, count = 0;
char **groups; char **groups;
FILE *fin;
int count = 0;
size_t len = 0;
char *line = NULL;
fin = fopen(LXC_USERNIC_CONF, "r");
if (!fin) { if (!fin) {
fprintf(stderr, "Failed to open %s: %s\n", LXC_USERNIC_CONF, usernic_error("Failed to open \"%s\": %s.\n", LXC_USERNIC_CONF, strerror(errno));
strerror(errno));
return -1; return -1;
} }
groups = get_groupnames(); groups = get_groupnames();
while ((getline(&line, &len, fin)) != -1) { while ((getline(&line, &len, fin)) != -1) {
ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name, type, br, &n); ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name, type, br, &n);
if (ret != 4) if (ret != 4)
continue; continue;
if (strlen(name) == 0) if (strlen(name) == 0)
continue; continue;
if (strcmp(name, me) != 0) if (strcmp(name, me)) {
{
if (name[0] != '@') if (name[0] != '@')
continue; continue;
if (!name_is_in_groupnames(name+1, groups))
if (!name_is_in_groupnames(name + 1, groups))
continue; continue;
} }
if (strcmp(type, intype) != 0)
if (strcmp(type, intype))
continue; continue;
if (strcmp(link, br) != 0)
if (strcmp(link, br))
continue; continue;
/* found the user or group with the appropriate settings, therefore finish the search. /* Found the user or group with the appropriate settings,
* what to do if there are more than one applicable lines? not specified in the docs. * therefore finish the search. What to do if there are more
* since getline is implemented with realloc, we don't need to free line until exiting func. * 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.
* *
* if append_alloted returns NULL, e.g. due to a malloc error, we set count to 0 and break the loop, * If append_alloted returns NULL, e.g. due to a malloc error,
* allowing cleanup and then exiting from main() * we set count to 0 and break the loop, allowing cleanup and
* then exiting from main().
*/ */
if (append_alloted(alloted, name, n) == NULL) { if (!append_alloted(alloted, name, n)) {
count = 0; count = 0;
break; break;
} }
...@@ -321,20 +327,20 @@ static int get_alloted(char *me, char *intype, char *link, struct alloted_s **al ...@@ -321,20 +327,20 @@ static int get_alloted(char *me, char *intype, char *link, struct alloted_s **al
fclose(fin); fclose(fin);
free(line); free(line);
// now return the total number of nics that this user can create /* Now return the total number of nics that this user can create. */
return count; return count;
} }
static char *get_eol(char *s, char *e) static char *get_eol(char *s, char *e)
{ {
while (s<e && *s && *s != '\n') while ((s < e) && *s && (*s != '\n'))
s++; s++;
return s; return s;
} }
static char *get_eow(char *s, char *e) static char *get_eow(char *s, char *e)
{ {
while (s<e && *s && !isblank(*s) && *s != '\n') while ((s < e) && *s && !isblank(*s) && (*s != '\n'))
s++; s++;
return s; return s;
} }
...@@ -343,24 +349,34 @@ static char *find_line(char *p, char *e, char *u, char *t, char *l) ...@@ -343,24 +349,34 @@ static char *find_line(char *p, char *e, char *u, char *t, char *l)
{ {
char *p1, *p2, *ret; char *p1, *p2, *ret;
while (p<e && (p1 = get_eol(p, e)) < e) { while ((p < e) && (p1 = get_eol(p, e)) < e) {
ret = p; ret = p;
if (*p == '#') if (*p == '#')
goto next; goto next;
while (p<e && isblank(*p)) p++;
while ((p < e) && isblank(*p))
p++;
p2 = get_eow(p, e); p2 = get_eow(p, e);
if (!p2 || p2-p != strlen(u) || strncmp(p, u, strlen(u)) != 0) if (!p2 || ((size_t)(p2 - p)) != strlen(u) || strncmp(p, u, strlen(u)))
goto next; goto next;
p = p2+1;
while (p<e && isblank(*p)) p++; p = p2 + 1;
while ((p < e) && isblank(*p))
p++;
p2 = get_eow(p, e); p2 = get_eow(p, e);
if (!p2 || p2-p != strlen(t) || strncmp(p, t, strlen(t)) != 0) if (!p2 || ((size_t)(p2 - p)) != strlen(t) || strncmp(p, t, strlen(t)))
goto next; goto next;
p = p2+1;
while (p<e && isblank(*p)) p++; p = p2 + 1;
while ((p < e) && isblank(*p))
p++;
p2 = get_eow(p, e); p2 = get_eow(p, e);
if (!p2 || p2-p != strlen(l) || strncmp(p, l, strlen(l)) != 0) if (!p2 || ((size_t)(p2 - p)) != strlen(l) || strncmp(p, l, strlen(l)))
goto next; goto next;
return ret; return ret;
next: next:
p = p1 + 1; p = p1 + 1;
...@@ -375,14 +391,17 @@ static bool nic_exists(char *nic) ...@@ -375,14 +391,17 @@ static bool nic_exists(char *nic)
int ret; int ret;
struct stat sb; struct stat sb;
if (strcmp(nic, "none") == 0) if (!strcmp(nic, "none"))
return true; return true;
ret = snprintf(path, MAXPATHLEN, "/sys/class/net/%s", nic); ret = snprintf(path, MAXPATHLEN, "/sys/class/net/%s", nic);
if (ret < 0 || ret >= MAXPATHLEN) // should never happen! if (ret < 0 || ret >= MAXPATHLEN)
return false; return false;
ret = stat(path, &sb); ret = stat(path, &sb);
if (ret != 0) if (ret < 0)
return false; return false;
return true; return true;
} }
...@@ -392,68 +411,81 @@ static int instantiate_veth(char *n1, char **n2) ...@@ -392,68 +411,81 @@ static int instantiate_veth(char *n1, char **n2)
err = snprintf(*n2, IFNAMSIZ, "%sp", n1); err = snprintf(*n2, IFNAMSIZ, "%sp", n1);
if (err < 0 || err >= IFNAMSIZ) { if (err < 0 || err >= IFNAMSIZ) {
fprintf(stderr, "nic name too long\n"); usernic_error("%s\n", "Could not create nic name.");
return -1; return -1;
} }
err = lxc_veth_create(n1, *n2); err = lxc_veth_create(n1, *n2);
if (err) { if (err) {
fprintf(stderr, "failed to create %s-%s : %s\n", n1, *n2, usernic_error("Failed to create %s-%s : %s.\n", n1, *n2, strerror(-err));
strerror(-err));
return -1; return -1;
} }
/* changing the high byte of the mac address to 0xfe, the bridge interface /* Changing the high byte of the mac address to 0xfe, the bridge
* will always keep the host's mac address and not take the mac address * interface will always keep the host's mac address and not take the
* of a container */ * mac address of a container. */
err = setup_private_host_hw_addr(n1); err = setup_private_host_hw_addr(n1);
if (err) { if (err)
fprintf(stderr, "failed to change mac address of host interface '%s' : %s\n", usernic_error("Failed to change mac address of host interface "
"%s : %s.\n",
n1, strerror(-err)); n1, strerror(-err));
}
return netdev_set_flag(n1, IFF_UP); return netdev_set_flag(n1, IFF_UP);
} }
static int get_mtu(char *name) static int get_mtu(char *name)
{ {
int idx = if_nametoindex(name); int idx;
idx = if_nametoindex(name);
return netdev_get_mtu(idx); return netdev_get_mtu(idx);
} }
static bool create_nic(char *nic, char *br, int pid, char **cnic) static bool create_nic(char *nic, char *br, int pid, char **cnic)
{ {
char *veth1buf, *veth2buf; char *veth1buf, *veth2buf;
int mtu, ret;
veth1buf = alloca(IFNAMSIZ); veth1buf = alloca(IFNAMSIZ);
veth2buf = alloca(IFNAMSIZ); veth2buf = alloca(IFNAMSIZ);
int ret, mtu; if (!veth1buf || !veth2buf) {
usernic_error("Failed allocate memory: %s.\n", strerror(errno));
return false;
}
ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic); ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic);
if (ret < 0 || ret >= IFNAMSIZ) { if (ret < 0 || ret >= IFNAMSIZ) {
fprintf(stderr, "host nic name too long\n"); usernic_error("%s", "Could not create nic name.\n");
return false; return false;
} }
/* create the nics */ /* create the nics */
if (instantiate_veth(veth1buf, &veth2buf) < 0) { if (instantiate_veth(veth1buf, &veth2buf) < 0) {
fprintf(stderr, "Error creating veth tunnel\n"); usernic_error("%s", "Error creating veth tunnel.\n");
return false; return false;
} }
if (strcmp(br, "none") != 0) { if (strcmp(br, "none")) {
/* copy the bridge's mtu to both ends */ /* copy the bridge's mtu to both ends */
mtu = get_mtu(br); mtu = get_mtu(br);
if (mtu != -1) { if (mtu > 0) {
if (lxc_netdev_set_mtu(veth1buf, mtu) < 0 || ret = lxc_netdev_set_mtu(veth1buf, mtu);
lxc_netdev_set_mtu(veth2buf, mtu) < 0) { if (ret < 0) {
fprintf(stderr, "Failed setting mtu\n"); usernic_error("Failed to set mtu to %d on %s.\n", mtu, veth1buf);
goto out_del;
}
ret = lxc_netdev_set_mtu(veth2buf, mtu);
if (ret < 0) {
usernic_error("Failed to set mtu to %d on %s.\n", mtu, veth2buf);
goto out_del; goto out_del;
} }
} }
/* attach veth1 to bridge */ /* attach veth1 to bridge */
if (lxc_bridge_attach(lxcpath, lxcname, br, veth1buf) < 0) { ret = lxc_bridge_attach(lxcpath, lxcname, br, veth1buf);
fprintf(stderr, "Error attaching %s to %s\n", veth1buf, br); if (ret < 0) {
usernic_error("Error attaching %s to %s.\n", veth1buf, br);
goto out_del; goto out_del;
} }
} }
...@@ -461,10 +493,16 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic) ...@@ -461,10 +493,16 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic)
/* pass veth2 to target netns */ /* pass veth2 to target netns */
ret = lxc_netdev_move_by_name(veth2buf, pid, NULL); ret = lxc_netdev_move_by_name(veth2buf, pid, NULL);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "Error moving %s to netns %d\n", veth2buf, pid); usernic_error("Error moving %s to network namespace of %d.\n", veth2buf, pid);
goto out_del; goto out_del;
} }
*cnic = strdup(veth2buf); *cnic = strdup(veth2buf);
if (!*cnic) {
usernic_error("Failed to copy string \"%s\".\n", veth2buf);
return false;
}
return true; return true;
out_del: out_del:
...@@ -474,29 +512,34 @@ out_del: ...@@ -474,29 +512,34 @@ out_del:
/* /*
* Get a new nic. * Get a new nic.
* *dest will container the name (vethXXXXXX) which is attached * *dest will contain the name (vethXXXXXX) which is attached
* on the host to the lxc bridge * on the host to the lxc bridge
*/ */
static bool get_new_nicname(char **dest, char *br, int pid, char **cnic) static bool get_new_nicname(char **dest, char *br, int pid, char **cnic)
{ {
int ret;
char template[IFNAMSIZ]; char template[IFNAMSIZ];
snprintf(template, sizeof(template), "vethXXXXXX");
*dest = lxc_mkifname(template);
if (!create_nic(*dest, br, pid, cnic)) { ret = snprintf(template, sizeof(template), "vethXXXXXX");
if (ret < 0 || (size_t)ret >= sizeof(template))
return false; return false;
}
*dest = lxc_mkifname(template);
if (!create_nic(*dest, br, pid, cnic))
return false;
return true; return true;
} }
static bool get_nic_from_line(char *p, char **nic) static bool get_nic_from_line(char *p, char **nic)
{ {
char user[100], type[100], br[100];
int ret; int ret;
char user[100], type[100], br[100];
ret = sscanf(p, "%99[^ \t\n] %99[^ \t\n] %99[^ \t\n] %99[^ \t\n]", user, type, br, *nic); ret = sscanf(p, "%99[^ \t\n] %99[^ \t\n] %99[^ \t\n] %99[^ \t\n]", user, type, br, *nic);
if (ret != 4) if (ret != 4)
return false; return false;
return true; return true;
} }
...@@ -508,35 +551,42 @@ struct entry_line { ...@@ -508,35 +551,42 @@ struct entry_line {
static bool cull_entries(int fd, char *me, char *t, char *br) static bool cull_entries(int fd, char *me, char *t, char *br)
{ {
struct stat sb; int i, n = 0;
char *buf, *p, *e, *nic;
off_t len; off_t len;
char *buf, *p, *e, *nic;
struct stat sb;
struct entry_line *entry_lines = NULL; struct entry_line *entry_lines = NULL;
int i, n = 0;
nic = alloca(100); nic = alloca(100);
if (!nic)
return false;
if (fstat(fd, &sb) < 0) { if (fstat(fd, &sb) < 0) {
fprintf(stderr, "Failed to fstat: %s\n", strerror(errno)); usernic_error("Failed to fstat: %s.\n", strerror(errno));
return false; return false;
} }
len = sb.st_size; len = sb.st_size;
if (len == 0) if (len == 0)
return true; return true;
buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) { if (buf == MAP_FAILED) {
fprintf(stderr, "Failed to create mapping: %s\n", strerror(errno)); usernic_error("Failed to establish shared memory mapping: %s.\n", strerror(errno));
return false; return false;
} }
p = buf; p = buf;
e = buf + len; e = buf + len;
while ((p = find_line(p, e, me, t, br)) != NULL) { while ((p = find_line(p, e, me, t, br))) {
struct entry_line *newe = realloc(entry_lines, sizeof(*entry_lines)*(n+1)); struct entry_line *newe;
newe = realloc(entry_lines, sizeof(*entry_lines) * (n + 1));
if (!newe) { if (!newe) {
free(entry_lines); free(entry_lines);
return false; return false;
} }
entry_lines = newe; entry_lines = newe;
entry_lines[n].start = p; entry_lines[n].start = p;
entry_lines[n].len = get_eol(p, e) - entry_lines[n].start; entry_lines[n].len = get_eol(p, e) - entry_lines[n].start;
...@@ -544,35 +594,43 @@ static bool cull_entries(int fd, char *me, char *t, char *br) ...@@ -544,35 +594,43 @@ static bool cull_entries(int fd, char *me, char *t, char *br)
n++; n++;
if (!get_nic_from_line(p, &nic)) if (!get_nic_from_line(p, &nic))
continue; continue;
if (nic && !nic_exists(nic)) if (nic && !nic_exists(nic))
entry_lines[n-1].keep = false; entry_lines[n - 1].keep = false;
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)
continue; continue;
memcpy(p, entry_lines[i].start, entry_lines[i].len); memcpy(p, entry_lines[i].start, entry_lines[i].len);
p += entry_lines[i].len; p += entry_lines[i].len;
*p = '\n'; *p = '\n';
p++; p++;
} }
free(entry_lines); free(entry_lines);
munmap(buf, sb.st_size); munmap(buf, sb.st_size);
if (ftruncate(fd, p-buf)) if (ftruncate(fd, p - buf))
fprintf(stderr, "Failed to set new file size\n"); usernic_error("Failed to set new file size: %s.\n", strerror(errno));
return true; return true;
} }
static int count_entries(char *buf, off_t len, char *me, char *t, char *br) static int count_entries(char *buf, off_t len, char *me, char *t, char *br)
{ {
char *e = &buf[len]; char *e;
int count = 0; int count = 0;
while ((buf = find_line(buf, e, me, t, br)) != NULL) {
e = &buf[len];
while ((buf = find_line(buf, e, me, t, br))) {
count++; count++;
buf = get_eol(buf, e)+1; buf = get_eol(buf, e) + 1;
if (buf >= e) if (buf >= e)
break; break;
} }
...@@ -584,16 +642,19 @@ static int count_entries(char *buf, off_t len, char *me, char *t, char *br) ...@@ -584,16 +642,19 @@ 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, struct alloted_s *names, 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)
{ {
int ret;
off_t len, slen; off_t len, slen;
char *newline, *owner;
struct stat sb; struct stat sb;
char *buf = NULL, *newline;
int ret, count = 0;
char *owner;
struct alloted_s *n; struct alloted_s *n;
int count = 0;
char *buf = NULL;
for (n=names; n!=NULL; n=n->next) for (n = names; n != NULL; n = n->next)
cull_entries(fd, n->name, intype, br); cull_entries(fd, n->name, intype, br);
if (allowed == 0) if (allowed == 0)
...@@ -602,19 +663,20 @@ static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid, char *int ...@@ -602,19 +663,20 @@ static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid, char *int
owner = names->name; owner = names->name;
if (fstat(fd, &sb) < 0) { if (fstat(fd, &sb) < 0) {
fprintf(stderr, "Failed to fstat: %s\n", strerror(errno)); usernic_error("Failed to fstat: %s.\n", strerror(errno));
return false; return false;
} }
len = sb.st_size; len = sb.st_size;
if (len != 0) { if (len > 0) {
buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) { if (buf == MAP_FAILED) {
fprintf(stderr, "Failed to create mapping\n"); usernic_error("Failed to establish shared memory mapping: %s.\n", strerror(errno));
return false; return false;
} }
owner = NULL; owner = NULL;
for (n=names; n!=NULL; n=n->next) { for (n = names; n != NULL; n = n->next) {
count = count_entries(buf, len, n->name, intype, br); count = count_entries(buf, len, n->name, intype, br);
if (count >= n->allowed) if (count >= n->allowed)
...@@ -630,49 +692,64 @@ static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid, char *int ...@@ -630,49 +692,64 @@ static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid, char *int
if (!get_new_nicname(nicname, br, pid, cnic)) if (!get_new_nicname(nicname, br, pid, cnic))
return false; return false;
/* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */ /* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(*nicname) + 5; slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(*nicname) + 5;
newline = alloca(slen); newline = alloca(slen);
if (!newline) {
usernic_error("Failed allocate memory: %s.\n", strerror(errno));
return false;
}
ret = snprintf(newline, slen, "%s %s %s %s\n", owner, 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); usernic_error("Error unlinking %s.\n", *nicname);
return false; return false;
} }
if (len) if (len)
munmap(buf, len); munmap(buf, len);
if (ftruncate(fd, len + slen)) if (ftruncate(fd, len + slen))
fprintf(stderr, "Failed to set new file size\n"); usernic_error("Failed to set new file size: %s.\n", strerror(errno));
buf = mmap(NULL, len + slen, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); buf = mmap(NULL, len + slen, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) { if (buf == MAP_FAILED) {
fprintf(stderr, "Failed to create mapping after extending: %s\n", strerror(errno)); usernic_error("Failed to establish shared memory mapping: %s.\n", strerror(errno));
if (lxc_netdev_delete_by_name(*nicname) != 0) if (lxc_netdev_delete_by_name(*nicname) != 0)
fprintf(stderr, "Error unlinking %s!\n", *nicname); usernic_error("Error unlinking %s.\n", *nicname);
return false; return false;
} }
strcpy(buf+len, newline);
munmap(buf, len+slen); strcpy(buf + len, newline);
munmap(buf, len + slen);
return true; return true;
} }
static bool create_db_dir(char *fnam) static bool create_db_dir(char *fnam)
{ {
char *p = alloca(strlen(fnam)+1); char *p;
p = alloca(strlen(fnam) + 1);
strcpy(p, fnam); strcpy(p, fnam);
fnam = p; fnam = p;
p = p + 1; p = p + 1;
again: again:
while (*p && *p != '/') p++; while (*p && *p != '/')
p++;
if (!*p) if (!*p)
return true; return true;
*p = '\0'; *p = '\0';
if (mkdir(fnam, 0755) && errno != EEXIST) { if (mkdir(fnam, 0755) && errno != EEXIST) {
fprintf(stderr, "failed to create %s\n", fnam); usernic_error("Failed to create %s: %s.\n", fnam, strerror(errno));
*p = '/'; *p = '/';
return false; return false;
} }
*(p++) = '/'; *(p++) = '/';
goto again; goto again;
} }
...@@ -801,61 +878,78 @@ static bool may_access_netns(int pid) ...@@ -801,61 +878,78 @@ static bool may_access_netns(int pid)
bool may_access = false; bool may_access = false;
ret = getresuid(&ruid, &euid, &suid); ret = getresuid(&ruid, &euid, &suid);
if (ret) { if (ret < 0) {
fprintf(stderr, "Failed to get my uids: %s\n", strerror(errno)); usernic_error("Failed to retrieve real, effective, and saved "
"user IDs: %s\n",
strerror(errno));
return false; return false;
} }
ret = setresuid(ruid, ruid, euid); ret = setresuid(ruid, ruid, euid);
if (ret) { if (ret < 0) {
fprintf(stderr, "Failed to set temp uids to (%d,%d,%d): %s\n", usernic_error("Failed to drop privilege by setting effective "
(int)ruid, (int)ruid, (int)euid, strerror(errno)); "user id and real user id to %d, and saved user "
"ID to %d: %s.\n",
ruid, euid, strerror(errno));
return false; return false;
} }
ret = snprintf(s, 200, "/proc/%d/ns/net", pid); ret = snprintf(s, 200, "/proc/%d/ns/net", pid);
if (ret < 0 || ret >= 200) // can't happen if (ret < 0 || ret >= 200)
return false; return false;
ret = access(s, R_OK); ret = access(s, R_OK);
if (ret) { may_access = true;
fprintf(stderr, "Uid %d may not access %s: %s\n", if (ret < 0) {
(int)ruid, s, strerror(errno)); may_access = false;
usernic_error("Uid %d may not access %s: %s\n", (int)ruid, s, strerror(errno));
} }
may_access = ret == 0;
ret = setresuid(ruid, euid, suid); ret = setresuid(ruid, euid, suid);
if (ret) { if (ret < 0) {
fprintf(stderr, "Failed to restore uids to (%d,%d,%d): %s\n", usernic_error("Failed to restore user id to %d, real user id "
(int)ruid, (int)euid, (int)suid, strerror(errno)); "to %d, and saved user ID to %d: %s.\n",
ruid, euid, suid, strerror(errno));
may_access = false; may_access = false;
} }
return may_access; return may_access;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int n, fd; int n, fd;
bool gotone = false;
char *me; char *me;
char *nicname = alloca(40); char *nicname;
char *cnic = NULL; // created nic name in container is returned here.
char *vethname = NULL;
int pid; int pid;
char *cnic = NULL; /* Created nic name in container is returned here. */
char *vethname = NULL;
bool gotone = false;
struct alloted_s *alloted = NULL; struct alloted_s *alloted = NULL;
nicname = alloca(40);
if (!nicname) {
usernic_error("Failed allocate memory: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* set a sane env, because we are setuid-root */ /* set a sane env, because we are setuid-root */
if (clearenv() < 0) { if (clearenv() < 0) {
fprintf(stderr, "Failed to clear environment"); usernic_error("%s", "Failed to clear environment.\n");
exit(1); exit(EXIT_FAILURE);
} }
if (setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1) < 0) { if (setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1) < 0) {
fprintf(stderr, "Failed to set PATH, exiting\n"); usernic_error("%s", "Failed to set PATH, exiting.\n");
exit(1); exit(EXIT_FAILURE);
} }
if ((me = get_username()) == NULL) { if ((me = get_username()) == NULL) {
fprintf(stderr, "Failed to get username\n"); usernic_error("%s", "Failed to get username.\n");
exit(1); exit(EXIT_FAILURE);
} }
if (argc < 6) if (argc < 6)
usage(argv[0], true); usage(argv[0], true);
if (argc >= 7) if (argc >= 7)
vethname = argv[6]; vethname = argv[6];
...@@ -863,26 +957,25 @@ int main(int argc, char *argv[]) ...@@ -863,26 +957,25 @@ int main(int argc, char *argv[])
lxcname = argv[2]; lxcname = argv[2];
errno = 0; errno = 0;
pid = (int) strtol(argv[3], NULL, 10); pid = strtol(argv[3], NULL, 10);
if (errno) { if (errno) {
fprintf(stderr, "Could not read pid: %s\n", argv[1]); usernic_error("Could not read pid: %s.\n", argv[1]);
exit(1); exit(EXIT_FAILURE);
} }
if (!create_db_dir(LXC_USERNIC_DB)) { if (!create_db_dir(LXC_USERNIC_DB)) {
fprintf(stderr, "Failed to create directory for db file\n"); usernic_error("%s", "Failed to create directory for db file.\n");
exit(1); exit(EXIT_FAILURE);
} }
if ((fd = open_and_lock(LXC_USERNIC_DB)) < 0) { if ((fd = open_and_lock(LXC_USERNIC_DB)) < 0) {
fprintf(stderr, "Failed to lock %s\n", LXC_USERNIC_DB); usernic_error("Failed to lock %s.\n", LXC_USERNIC_DB);
exit(1); exit(EXIT_FAILURE);
} }
if (!may_access_netns(pid)) { if (!may_access_netns(pid)) {
fprintf(stderr, "User %s may not modify netns for pid %d\n", usernic_error("User %s may not modify netns for pid %d.\n", me, pid);
me, pid); exit(EXIT_FAILURE);
exit(1);
} }
n = get_alloted(me, argv[4], argv[5], &alloted); n = get_alloted(me, argv[4], argv[5], &alloted);
...@@ -892,17 +985,19 @@ int main(int argc, char *argv[]) ...@@ -892,17 +985,19 @@ int main(int argc, char *argv[])
close(fd); close(fd);
free_alloted(&alloted); free_alloted(&alloted);
if (!gotone) { if (!gotone) {
fprintf(stderr, "Quota reached\n"); usernic_error("%s", "Quota reached.\n");
exit(1); exit(EXIT_FAILURE);
} }
// Now rename the link /* Now rename the link. */
if (rename_in_ns(pid, cnic, &vethname) < 0) { if (rename_in_ns(pid, cnic, &vethname) < 0) {
fprintf(stderr, "Failed to rename the link\n"); usernic_error("%s", "Failed to rename the link.\n");
exit(1); exit(EXIT_FAILURE);
} }
// write the name of the interface pair to the stdout - like eth0:veth9MT2L4 /* Write the name of the interface pair to the stdout - like
* eth0:veth9MT2L4.
*/
fprintf(stdout, "%s:%s\n", vethname, nicname); fprintf(stdout, "%s:%s\n", vethname, nicname);
exit(0); exit(EXIT_SUCCESS);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment