The default password in a new container is now auto-generated using phoneme rules and (good) random numbers.
Even if the default random password is set in a distribution-specific template and you use the download template to pull a pre-built rootfs image, you will get the same password every time unless the pre-built rootfs image is updated. So, the default random password in a new container is to be set after container creation. The user names whose passwords to be changed are stored in *.chpasswd file which is located at /usr/share/lxc/config. Each line of the file specifies a user name whose password is to be changed. If the target *.chpasswd file does not exist, no password is changed in a new container. Signed-off-by: TAMUKI Shoichi <tam...@linet.gr.jp> --- config/templates/Makefile.am | 27 +++- config/templates/altlinux.chpasswd | 1 + config/templates/archlinux.chpasswd | 1 + config/templates/busybox.chpasswd | 1 + config/templates/centos.chpasswd | 1 + config/templates/debian.chpasswd | 1 + config/templates/fedora.chpasswd | 1 + config/templates/gentoo.chpasswd | 1 + config/templates/openmandriva.chpasswd | 1 + config/templates/opensuse.chpasswd | 1 + config/templates/oracle.chpasswd | 2 + config/templates/plamo.chpasswd | 1 + config/templates/ubuntu.chpasswd | 1 + src/lxc/Makefile.am | 2 + src/lxc/lxccontainer.c | 218 ++++++++++++++++++++++++++++++++- src/lxc/pwgen.c | 201 ++++++++++++++++++++++++++++++ src/lxc/pwgen.h | 26 ++++ 17 files changed, 483 insertions(+), 4 deletions(-) create mode 100644 config/templates/altlinux.chpasswd create mode 100644 config/templates/archlinux.chpasswd create mode 100644 config/templates/busybox.chpasswd create mode 100644 config/templates/centos.chpasswd create mode 100644 config/templates/debian.chpasswd create mode 100644 config/templates/fedora.chpasswd create mode 100644 config/templates/gentoo.chpasswd create mode 100644 config/templates/openmandriva.chpasswd create mode 100644 config/templates/opensuse.chpasswd create mode 100644 config/templates/oracle.chpasswd create mode 100644 config/templates/plamo.chpasswd create mode 100644 config/templates/ubuntu.chpasswd create mode 100644 src/lxc/pwgen.c create mode 100644 src/lxc/pwgen.h diff --git a/config/templates/Makefile.am b/config/templates/Makefile.am index 82ca8be..22b231f 100644 --- a/config/templates/Makefile.am +++ b/config/templates/Makefile.am @@ -1,30 +1,55 @@ templatesconfigdir=@LXCTEMPLATECONFIG@ -EXTRA_DIST = common.seccomp +EXTRA_DIST = \ + altlinux.chpasswd \ + archlinux.chpasswd \ + busybox.chpasswd \ + centos.chpasswd \ + common.seccomp \ + debian.chpasswd \ + fedora.chpasswd \ + gentoo.chpasswd \ + openmandriva.chpasswd \ + opensuse.chpasswd \ + oracle.chpasswd \ + plamo.chpasswd \ + ubuntu.chpasswd templatesconfig_DATA = \ + altlinux.chpasswd \ + archlinux.chpasswd \ archlinux.common.conf \ archlinux.userns.conf \ + busybox.chpasswd \ + centos.chpasswd \ centos.common.conf \ centos.userns.conf \ common.conf \ common.seccomp \ + debian.chpasswd \ debian.common.conf \ debian.userns.conf \ + fedora.chpasswd \ fedora.common.conf \ fedora.userns.conf \ + gentoo.chpasswd \ gentoo.common.conf \ gentoo.moresecure.conf \ gentoo.userns.conf \ + openmandriva.chpasswd \ + opensuse.chpasswd \ opensuse.common.conf \ opensuse.userns.conf \ + oracle.chpasswd \ oracle.common.conf \ oracle.userns.conf \ + plamo.chpasswd \ plamo.common.conf \ plamo.userns.conf \ ubuntu-cloud.common.conf \ ubuntu-cloud.lucid.conf \ ubuntu-cloud.userns.conf \ + ubuntu.chpasswd ubuntu.common.conf \ ubuntu.lucid.conf \ ubuntu.userns.conf \ diff --git a/config/templates/altlinux.chpasswd b/config/templates/altlinux.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/altlinux.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/archlinux.chpasswd b/config/templates/archlinux.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/archlinux.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/busybox.chpasswd b/config/templates/busybox.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/busybox.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/centos.chpasswd b/config/templates/centos.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/centos.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/debian.chpasswd b/config/templates/debian.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/debian.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/fedora.chpasswd b/config/templates/fedora.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/fedora.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/gentoo.chpasswd b/config/templates/gentoo.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/gentoo.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/openmandriva.chpasswd b/config/templates/openmandriva.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/openmandriva.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/opensuse.chpasswd b/config/templates/opensuse.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/opensuse.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/oracle.chpasswd b/config/templates/oracle.chpasswd new file mode 100644 index 0000000..e4b3edb --- /dev/null +++ b/config/templates/oracle.chpasswd @@ -0,0 +1,2 @@ +root +oracle diff --git a/config/templates/plamo.chpasswd b/config/templates/plamo.chpasswd new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/config/templates/plamo.chpasswd @@ -0,0 +1 @@ +root diff --git a/config/templates/ubuntu.chpasswd b/config/templates/ubuntu.chpasswd new file mode 100644 index 0000000..e9e5f7c --- /dev/null +++ b/config/templates/ubuntu.chpasswd @@ -0,0 +1 @@ +ubuntu diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index da3f78e..fe6793f 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -89,6 +89,7 @@ liblxc_so_SOURCES = \ lxcutmp.c lxcutmp.h \ lxclock.h lxclock.c \ lxccontainer.c lxccontainer.h \ + pwgen.c pwgen.h \ version.h \ \ $(LSM_SOURCES) @@ -117,6 +118,7 @@ AM_CFLAGS=-I$(top_srcdir)/src \ -DLXCINITDIR=\"$(LXCINITDIR)\" \ -DLIBEXECDIR=\"$(LIBEXECDIR)\" \ -DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\" \ + -DLXCTEMPLATECONFIG=\"$(LXCTEMPLATECONFIG)\" \ -DLOGPATH=\"$(LOGPATH)\" \ -DLXC_DEFAULT_CONFIG=\"$(LXC_DEFAULT_CONFIG)\" \ -DLXC_USERNIC_DB=\"$(LXC_USERNIC_DB)\" \ diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index dbbd24a..2301355 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -56,6 +56,7 @@ #include "namespace.h" #include "lxclock.h" #include "sync.h" +#include "pwgen.h" #if HAVE_IFADDRS_H #include <ifaddrs.h> @@ -868,6 +869,50 @@ static char *get_template_path(const char *t) return tpath; } +/* + * Given the '-t' template option to lxc-create, figure out what to + * do. If the template is a full executable path, just return NULL. + * If the template is 'download', use the effective template instead. + * If it is something like 'ubuntu', then return LXCTEMPLATECONFIG/ + * ubuntu.chpasswd. Return the chpasswd file, or return NULL if not + * exist. + */ +static char *get_chpasswd_path(const char *t, char *const argv[]) +{ + int i, ret, len; + const char *et = t; + char *p, *chpwpath; + + if (t[0] == '/') + return NULL; + if (!strcmp(t, "download")) { + for (i = 0, et = NULL; argv[i] && !et; i++) + if (!strcmp(argv[i], "-d")) + et = argv[i + 1]; + else if (!strncmp(argv[i], "--d", 3)) + et = ((p = strchr(argv[i], '=')) != NULL) + ? p + 1 : argv[i + 1]; + } else + et = t; + + len = strlen(LXCTEMPLATECONFIG) + strlen(et) + strlen(".chpasswd") + 2; + chpwpath = malloc(len); + if (!chpwpath) + return NULL; + ret = snprintf(chpwpath, len, "%s/%s.chpasswd", LXCTEMPLATECONFIG, et); + if (ret < 0 || ret >= len) { + free(chpwpath); + return NULL; + } + if (access(chpwpath, R_OK) < 0) { + NOTICE("nothing to do chpasswd: %s", et); + free(chpwpath); + return NULL; + } + + return chpwpath; +} + static char *lxcbasename(char *path) { char *p = path + strlen(path) - 1; @@ -880,8 +925,9 @@ static char *figureout_rootfs(struct lxc_conf *conf); static char **prepend_lxc_usernsexec(char **tpath, struct lxc_conf *conf, int nargs, char **newargv); -static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet, - char *const argv[]) +static bool create_run_template(struct lxc_container *c, char *tpath, + char **const uname, char **const passwd, bool quiet, + char *const argv[]) { pid_t pid; @@ -979,6 +1025,99 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet return false; } + if (!uname) + return true; + + pid = fork(); + if (pid < 0) { + SYSERROR("failed to fork task for chpasswd"); + return false; + } + + if (pid == 0) { // child + char *sharg, *rootfs; + char *chpasswd = NULL; + int i, j; + int ret, len, nargs = 0; + char *p; + char **newargv; + struct lxc_conf *conf = c->lxc_conf; + + if (quiet) { + close(0); + close(1); + close(2); + open("/dev/zero", O_RDONLY); + open("/dev/null", O_RDWR); + open("/dev/null", O_RDWR); + } + + rootfs=figureout_rootfs(conf); + + /* + * create our new array, pre-pend the template name and + * base args + */ + nargs += 3; // "sh", "-c" and + // "echo \"$chpasswd\" | chroot $rootfs /usr/sbin/chpasswd" + // args + + newargv = malloc(nargs * sizeof(*newargv)); + if (!newargv) + exit(1); + newargv[0] = "sh"; + newargv[1] = "-c"; + + for (i = j = 0; uname[i]; i++, j += len - 1) { + len = strlen(uname[i]) + strlen(passwd[i]) + 3; + chpasswd = realloc(chpasswd, j + len); + if (!chpasswd) + exit(1); + ret = snprintf(chpasswd + j, len, "%s:%s\n", + uname[i], passwd[i]); + if (ret < 0 || ret >= len) + exit(1); + } + if ((p = strrchr(chpasswd, '\n')) != NULL) + *p = '\0'; + + len = strlen("echo") + strlen(chpasswd) + + strlen("chroot") + strlen(rootfs) + + strlen("/usr/sbin/chpasswd") + 9; + sharg = malloc(len); + if (!sharg) + exit(1); + ret = snprintf(sharg, len, + "echo \"%s\" | chroot %s /usr/sbin/chpasswd", + chpasswd, rootfs); + if (ret < 0 || ret >= len) + exit(1); + newargv[2] = sharg; + + /* add trailing NULL */ + nargs++; + newargv = realloc(newargv, nargs * sizeof(*newargv)); + if (!newargv) + exit(1); + newargv[nargs - 1] = NULL; + + tpath = "sh"; + /* prepend the template command with lxc-usernsexec */ + if (!lxc_list_empty(&conf->id_map)) + newargv = prepend_lxc_usernsexec(&tpath, conf, + nargs, newargv); + + /* execute */ + execvp(tpath, newargv); + SYSERROR("failed to execute chpasswd"); + exit(1); + } + + if (wait_for_pid(pid) != 0) { + ERROR("chpasswd for %s failed", c->name); + return false; + } + return true; } @@ -1275,6 +1414,12 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, bool ret = false; pid_t pid; char *tpath = NULL; + char *chpwpath = NULL; + char **uname = NULL, **passwd = NULL; + int i; + FILE *fp; + char *p, *line = NULL; + size_t len = 0; int partial_fd; if (!c) @@ -1286,6 +1431,49 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, ERROR("bad template: %s", t); goto out; } + chpwpath = get_chpasswd_path(t, argv); + if (chpwpath) { + fp = fopen(chpwpath, "r"); + for (i = 0; getline(&line, &len, fp) != -1; i++) { + if ((p = strchr(line, '\n')) != NULL) + *p = '\0'; + if (!(uname = realloc(uname, + (i + 1) * sizeof(*uname)))) { + SYSERROR("out of memory"); + exit(1); + } + if (!(uname[i] = malloc(strlen(line) + 1))) { + SYSERROR("out of memory"); + exit(1); + } + strcpy(uname[i], line); + if (!(passwd = realloc(passwd, + (i + 1) * sizeof(*passwd)))) { + SYSERROR("out of memory"); + exit(1); + } + if (!(passwd[i] = malloc(11))) { + SYSERROR("out of memory"); + exit(1); + } + pw_phonemes(passwd[i], 10); + } + if (line) + free(line); + fclose(fp); + if (!(uname = realloc(uname, + (i + 1) * sizeof(*uname)))) { + SYSERROR("out of memory"); + exit(1); + } + uname[i] = NULL; + if (!(passwd = realloc(passwd, + (i + 1) * sizeof(*passwd)))) { + SYSERROR("out of memory"); + exit(1); + } + passwd[i] = NULL; + } } /* @@ -1375,9 +1563,17 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, if (!load_config_locked(c, c->configfile)) goto out_unlock; - if (!create_run_template(c, tpath, !!(flags & LXC_CREATE_QUIET), argv)) + if (!create_run_template(c, tpath, uname, passwd, + !!(flags & LXC_CREATE_QUIET), argv)) goto out_unlock; + if (uname) + for (i = 0; uname[i]; i++) + printf("The default %s password is: %s\n", + uname[i], passwd[i]); + else + printf("No password is changed.\n"); + // now clear out the lxc_conf we have, reload from the created // container lxcapi_clear_config(c); @@ -1399,6 +1595,22 @@ out: free_tpath: if (tpath) free(tpath); + if (chpwpath) + free(chpwpath); + if (uname) { + char **pp; + + for (pp = uname; *pp; pp++) + free(*pp); + free(uname); + } + if (passwd) { + char **pp; + + for (pp = passwd; *pp; pp++) + free(*pp); + free(passwd); + } return ret; } diff --git a/src/lxc/pwgen.c b/src/lxc/pwgen.c new file mode 100644 index 0000000..c5a83d0 --- /dev/null +++ b/src/lxc/pwgen.c @@ -0,0 +1,201 @@ +/* + * pwgen.c --- generate secure password using phoneme rules and + * (good) random numbers. + * + * Copyright (C) 2001,2002 by Theodore Ts'o + * Copyright (C) 2014 by TAMUKI Shoichi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "pwgen.h" + +struct pw_element { + const char *str; + int flags; +}; + +/* Flags for the pw_element */ +#define CONSONANT 0x0001 +#define VOWEL 0x0002 +#define DIPTHONG 0x0004 +#define NOT_FIRST 0x0008 + +struct pw_element elements[] = { + { "a", VOWEL }, + { "ae", VOWEL | DIPTHONG }, + { "ah", VOWEL | DIPTHONG }, + { "ai", VOWEL | DIPTHONG }, + { "b", CONSONANT }, + { "c", CONSONANT }, + { "ch", CONSONANT | DIPTHONG }, + { "d", CONSONANT }, + { "e", VOWEL }, + { "ee", VOWEL | DIPTHONG }, + { "ei", VOWEL | DIPTHONG }, + { "f", CONSONANT }, + { "g", CONSONANT }, + { "gh", CONSONANT | DIPTHONG | NOT_FIRST }, + { "h", CONSONANT }, + { "i", VOWEL }, + { "ie", VOWEL | DIPTHONG }, + { "j", CONSONANT }, + { "k", CONSONANT }, + { "l", CONSONANT }, + { "m", CONSONANT }, + { "n", CONSONANT }, + { "ng", CONSONANT | DIPTHONG | NOT_FIRST }, + { "o", VOWEL }, + { "oh", VOWEL | DIPTHONG }, + { "oo", VOWEL | DIPTHONG }, + { "p", CONSONANT }, + { "ph", CONSONANT | DIPTHONG }, + { "qu", CONSONANT | DIPTHONG }, + { "r", CONSONANT }, + { "s", CONSONANT }, + { "sh", CONSONANT | DIPTHONG }, + { "t", CONSONANT }, + { "th", CONSONANT | DIPTHONG }, + { "u", VOWEL }, + { "v", CONSONANT }, + { "w", CONSONANT }, + { "x", CONSONANT }, + { "y", CONSONANT }, + { "z", CONSONANT } +}; + +#define NUM_ELEMENTS (sizeof(elements) / sizeof(struct pw_element)) + +/* Flags for the pwgen function */ +#define PW_DIGITS 0x0001 /* At least one digit */ +#define PW_UPPERS 0x0002 /* At least one upper letter */ + +static int pw_number(int max_num); +static int get_random_fd(void); + +void pw_phonemes(char *buf, int size) +{ + int feature_flags, should_be, prev, first; + int c, i, len, flags; + const char *str; + +try_again: + feature_flags = PW_DIGITS | PW_UPPERS; + should_be = (pw_number(2)) ? VOWEL : CONSONANT; + prev = 0, first = 1; + c = 0; + while (c < size) { + str = elements[i = pw_number(NUM_ELEMENTS)].str; + len = strlen(str); + flags = elements[i].flags; + /* Don't allow us to overflow the buffer */ + if (c + len > size) + continue; + /* Filter on the basic type of the next element */ + if (!(flags & should_be)) + continue; + /* Handle the NOT_FIRST flag */ + if (first && flags & NOT_FIRST) + continue; + /* Don't allow VOWEL followed a Vowel/Dipthong pair */ + if (prev & VOWEL && flags & VOWEL && flags & DIPTHONG) + continue; + /* + * OK, we found an element which matches our criteria, + * let's do it! + */ + strcpy(buf + c, str); + if ((first || flags & CONSONANT) && !pw_number(5)) { + buf[c] = toupper(buf[c]); + feature_flags &= ~PW_UPPERS; + } + /* Time to stop? */ + if ((c += len) == size) + break; + if (!first && pw_number(10) < 3) { + buf[c++] = '0' + pw_number(10), buf[c] = 0; + feature_flags &= ~PW_DIGITS; + should_be = (pw_number(2)) ? VOWEL : CONSONANT; + prev = 0, first = 1; + continue; + } + /* OK, figure out what the next element should be */ + should_be = (should_be == CONSONANT) ? VOWEL + : ((prev & VOWEL || flags & DIPTHONG + || pw_number(5) < 2) ? VOWEL : CONSONANT); + prev = flags, first = 0; + } + if (feature_flags & (PW_DIGITS | PW_UPPERS)) + goto try_again; +} + +/* + * Generate a random number n, where 0 <= n < max_num, using + * /dev/urandom if possible. + */ +static int pw_number(int max_num) +{ + int fd, i; + unsigned int rand_num; + char *cp = (char *) &rand_num; + int nbytes = 4, lose_counter = 0; + + if ((fd = get_random_fd()) >= 0) + while (nbytes > 0) + if ((i = read(fd, cp, nbytes)) < 0 + && (errno == EINTR || errno == EAGAIN)) + continue; + else if (i <= 0) { + if (lose_counter++ == 8) + break; + } else + cp += i, nbytes -= i, lose_counter = 0; + close(fd); + if (nbytes == 0) + return rand_num % max_num; + /* OK, we weren't able to use /dev/random, fall back to rand/rand48 */ + return (int) (drand48() * max_num); +} + +/* Borrowed/adapted from e2fsprogs's UUID generation code */ +static int get_random_fd(void) +{ + static int fd = -2; + struct timeval tv; + int i; + + if (fd == -2) { + gettimeofday(&tv, 0); + if ((fd = open("/dev/urandom", O_RDONLY)) == -1) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + srand48(tv.tv_sec << 9 ^ tv.tv_usec >> 11 ^ getpid() + ^ getpgrp() << 15); + } + /* Crank the random number generator a few times */ + gettimeofday(&tv, 0); + for (i = 0; i < ((tv.tv_sec ^ tv.tv_usec) & 0x1f); i++) + drand48(); + return fd; +} + diff --git a/src/lxc/pwgen.h b/src/lxc/pwgen.h new file mode 100644 index 0000000..9d0adc5 --- /dev/null +++ b/src/lxc/pwgen.h @@ -0,0 +1,26 @@ +/* + * pwgen.h --- header file for password generator + * + * Copyright (C) 2001,2002 by Theodore Ts'o + * Copyright (C) 2014 by TAMUKI Shoichi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __PWGEN_H +#define __PWGEN_H +extern void pw_phonemes(char *buf, int size); +#endif + -- 1.9.0 _______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel