Exemple #1
0
int main(int argc, char **argv) {
  hardened_shadow_openlog("useradd");

  if (lckpwdf() != 0)
    err(EXIT_FAILURE, "lckpwdf");

  if (!read_defaults_file())
    warnx("failed to read defaults file");

  parse_args(argc, argv);

  if (flag_defaults)
    return (handle_flag_defaults()) ? EXIT_SUCCESS : EXIT_FAILURE;

  determine_uid_gid();
  if (!flag_no_log_init)
    initialize_lastlog();
  if (flag_create_home)
    create_home_dir();
  if (default_create_mail_spool && !flag_system)
    create_mail_spool();
  create_account();

  hardened_shadow_flush_nscd("passwd");
  hardened_shadow_flush_nscd("group");

  if (ulckpwdf() != 0)
    warn("ulckpwdf");

  return EXIT_SUCCESS;
}
int
lock_pwdf(void)
{
        int i;
        int retval;

#ifndef HELPER_COMPILE
        if (unix_selinux_confined()) {
                return PAM_SUCCESS;
        }
#endif
        /* These values for the number of attempts and the sleep time
           are, of course, completely arbitrary.
           My reading of the PAM docs is that, once pam_chauthtok() has been
           called with PAM_UPDATE_AUTHTOK, we are obliged to take any
           reasonable steps to make sure the token is updated; so retrying
           for 1/10 sec. isn't overdoing it. */
        i=0;
        while((retval = lckpwdf()) != 0 && i < 100) {
                usleep(1000);
                i++;
        }
        if(retval != 0) {
                return PAM_AUTHTOK_LOCK_BUSY;
        }
        return PAM_SUCCESS;
}
Exemple #3
0
uid_t SecurityManager::authenticate(char *userName, char *password) {
#ifdef __APPLE__
	// lckpwdf etc. are not supported by MacOS.
	return (uid_t)-1;
#else
	if ((userName == NULL) || (password == NULL))
		return (uid_t)-1;
	uid_t result = (uid_t)-1;
	lckpwdf();
	setpwent();
	setspent();
	struct passwd *passwdEntry = getpwnam(userName);
	struct spwd *shadowEntry = getspnam(userName);
	if ((shadowEntry != NULL) && (passwdEntry != NULL)) {
		char *plain = password;
		char *encrypted = shadowEntry->sp_pwdp;
		if (strcmp(crypt(plain, encrypted), encrypted) == 0)
			result = passwdEntry->pw_uid;
	}
	endpwent();
	endspent();
	ulckpwdf();
	return result;
#endif
} // end of authenticate(char*, char*)
Exemple #4
0
static VALUE
rb_shadow_lckpwdf(VALUE self)
{
  int result;
  result = lckpwdf();
  if( result == -1 )
    rb_raise(rb_eFileLock,"password file was locked");
  else
    return Qtrue;
};
Exemple #5
0
static void edit_file(int is_shadow)
{
	struct stat begin, end;
	int passwd_file, ch_ret;
	FILE *tmp_fd;

	pw_init();

	/* acquire exclusive lock */
	if (lckpwdf() < 0)
		err(EXIT_FAILURE, _("cannot get lock"));

	passwd_file = open(orig_file, O_RDONLY, 0);
	if (passwd_file < 0)
		err(EXIT_FAILURE, _("cannot open %s"), orig_file);
	tmp_fd = pw_tmpfile(passwd_file);

	if (fstat(fileno(tmp_fd), &begin))
		pw_error(tmp_file, 1, 1);

	pw_edit();

	if (fstat(fileno(tmp_fd), &end))
		pw_error(tmp_file, 1, 1);
	/* Some editors, such as Vim with 'writebackup' mode enabled,
	 * use "atomic save" in which the old file is deleted and a new
	 * one with the same name created in its place.  */
	if (end.st_nlink == 0) {
		if (close_stream(tmp_fd) != 0)
			err(EXIT_FAILURE, _("write error"));
		tmp_fd = fopen(tmp_file, "r");
		if (!tmp_file)
			err(EXIT_FAILURE, _("cannot open %s"), tmp_file);
		if (fstat(fileno(tmp_fd), &end))
			pw_error(tmp_file, 1, 1);
	}
	if (begin.st_mtime == end.st_mtime) {
		warnx(_("no changes made"));
		pw_error((char *)NULL, 0, 0);
	}
	/* pw_tmpfile() will create the file with mode 600 */
	if (!is_shadow)
		ch_ret = fchmod(fileno(tmp_fd), 0644);
	else
		ch_ret = fchmod(fileno(tmp_fd), 0400);
	if (ch_ret < 0)
		err(EXIT_FAILURE, "%s: %s", _("cannot chmod file"), orig_file);
	if (close_stream(tmp_fd) != 0)
		err(EXIT_FAILURE, _("write error"));
	pw_write();
	close(passwd_file);
	ulckpwdf();
}
Exemple #6
0
static VALUE
rb_shadow_lock_p(VALUE self)
{
  int result;

  result = lckpwdf();
  if( result == -1 ){
    return Qtrue;
  }
  else{
    ulckpwdf();
    return Qfalse;
  };
};
int
commonio_lock(struct commonio_db *db)
{
#ifdef HAVE_LCKPWDF
	/*
	 * only if the system libc has a real lckpwdf() - the one from
	 * lockpw.c calls us and would cause infinite recursion!
	 */

	/*
	 * Call lckpwdf() on the first lock.
	 * If it succeeds, call *_lock() only once
	 * (no retries, it should always succeed).
	 */
	if (lock_count == 0) {
		if (lckpwdf() == -1)
			return 0;  /* failure */
	}

	if (commonio_lock_nowait(db))
		return 1;  /* success */

	ulckpwdf();
	return 0;  /* failure */
#else
	int i;

	/*
	 * lckpwdf() not used - do it the old way.
	 */
#ifndef LOCK_TRIES
#define LOCK_TRIES 15
#endif

#ifndef LOCK_SLEEP
#define LOCK_SLEEP 1
#endif
	for (i = 0; i < LOCK_TRIES; i++) {
		if (i > 0)
			sleep(LOCK_SLEEP);  /* delay between retries */
		if (commonio_lock_nowait(db))
			return 1;  /* success */
		/* no unnecessary retries on "permission denied" errors */
		if (geteuid() != 0)
			return 0;
	}
	return 0;  /* failure */
#endif
}
Exemple #8
0
/*
 * lock functions for files repository
 */
int
files_lock(void)
{
	int res;

	if (lckpwdf()) {
		switch (errno) {
		case EINTR:
			res = PWU_BUSY;
			break;
		case EACCES:
			res = PWU_DENIED;
			break;
		case 0:
			res = PWU_SUCCESS;
			break;
		}
	} else
		res = PWU_SUCCESS;

	return (res);
}
Exemple #9
0
static VALUE
rb_shadow_lock(VALUE self)
{
  int result;

  if( rb_iterator_p() ){
    result = lckpwdf();
    if( result == -1 ){
      rb_raise(rb_eFileLock,"password file was locked");
    }
    else{
      in_lock++;
      rb_yield(Qnil);
      in_lock--;
      ulckpwdf();
    };
    return Qtrue;
  }
  else{
    return rb_shadow_lckpwdf(self);
  };
};
Exemple #10
0
int main(int argc, char **argv) {
  hardened_shadow_openlog("groupadd");

  if (lckpwdf() != 0)
    err(EXIT_FAILURE, "lckpwdf");

  parse_args(argc, argv);

  if (flag_gid == (gid_t)-1) {
    const char *gid_key = (flag_system) ? "SYSTEM_GID_RANGE" : "USER_GID_RANGE";
    intmax_t gid_min, gid_max;
    if (!hardened_shadow_config_get_range(gid_key, &gid_min, &gid_max))
      errx(EXIT_FAILURE, "Failed to retrieve GID range.");
    if (!hardened_shadow_allocate_gid(gid_min, gid_max, &flag_gid))
      errx(EXIT_FAILURE, "Failed to allocate GID.");
  }

  struct group grp;
  memset(&grp, '\0', sizeof(grp));

  grp.gr_name = group_name;
  grp.gr_passwd = HARDENED_SHADOW_SHADOW_PASSWD;
  grp.gr_gid = flag_gid;
  grp.gr_mem = &empty_list;

  if (!hardened_shadow_replace_group(group_name, &grp))
    errx(EXIT_FAILURE, "Failed to update /etc/group.");

  hardened_shadow_syslog(LOG_INFO, "new group: name=%s, GID=%ju",
                         group_name, (uintmax_t)flag_gid);

  hardened_shadow_flush_nscd("group");

  if (ulckpwdf() != 0)
    warn("ulckpwdf");

  return EXIT_SUCCESS;
}
Exemple #11
0
/*
 * This function looks up "username" in the shadow password file, determines
 * the hash algorithm type, and returns the salt and the password
 * hash for that user.
 * 
 * Given the salt and the user password, then the hash can be created.
 * The generated hash is used as an SRP password (client side), and
 * the generator for the SRP secret (server side).
 *
 * Crypt password file format references:
 * http://php.net/manual/en/function.crypt.php
 * http://en.wikipedia.org/wiki/Crypt_%28C%29#Blowfish-based_scheme
 *
 * Look up from the shadow password file the specified user, and if found,
 * return the salt field parsed out from the hash entry
 *
 * Algorithm ID
 * $1$  MD5
 * 12 characters salt follows
 *
 * $2a$ Blowfish
 * $2b$ Blowfish
 * $2x$ Blowfish
 * $2y$ Blowfish
 * Blowfish salt format:
 * $id$NN$-----22 chars-salt----++++++hash+++++:
 *
 * SHA salt format
 * $5$  SHA-256
 * $6$  SHA-512
 * $ID$salt$hash
 */
int get_sp_salt(const char *username,
                char **ret_salt,
                char **ret_encpwd)
{
    int st = 0;
    int is_locked = 0;
    struct spwd *spval = NULL;
    struct spwd spval_buf = {0};
    char *spbuf_str = NULL;
    int spbuf_str_len = 256;
    int salt_len = 0;
    char *salt = NULL;
    char *encpwd = NULL;
    char *sp = NULL;
    int cur_uid = 0;
    int error = 0;
    
    if (!username || !ret_salt || !ret_encpwd)
    {
        st = -1;
        errno = EINVAL;
        goto error;
    }

    /* Must be root to read shadow password file */
    cur_uid = getuid();
    error = seteuid(0);
    if (error != 0)
    {
        st = -1;
        goto error;
    }

    /* Obtain password file lock, and hold minimum amount of time */
    st = lckpwdf();
    if (st == -1)
    {
        goto error;
    }
    is_locked = 1;

    spbuf_str = calloc(spbuf_str_len, sizeof(char));
    if (!spbuf_str)
    {
        st = -1;
        goto error;
    }
    st = getspnam_r(username,
                    &spval_buf,
                    spbuf_str,
                    spbuf_str_len,
                    &spval);

    if (!spval || st == -1)
    {
        /* Failed due to permissions or entry not found */
        st = -1;
        goto error;
    }
    salt = strdup(spval->sp_pwdp);
    if (!salt)
    {
        /* errno is set */
        st = -1;
        goto error;
    }
    encpwd = strdup(spval->sp_pwdp);
    if (!encpwd)
    {
        /* errno is set */
        st = -1;
        goto error;
    }
    ulckpwdf();
    error = seteuid(cur_uid);
    if (error != 0)
    {
        st = -1;
        goto error;
    }

    is_locked = 0;
   
    /* CRYPT_DES hash is not supported; how to test? */

    /* Determine the hash algorithn, and therefore the salt length */
    if (!strncmp(salt, CRYPT_MD5, strlen(CRYPT_MD5)))
    {
        /* $1$123456789012 */
        salt_len = 12 + 3;
    }
    else if (!strncmp(salt, CRYPT_BLOWFISH_2A, strlen(CRYPT_BLOWFISH_2A)) ||
             !strncmp(salt, CRYPT_BLOWFISH_2B, strlen(CRYPT_BLOWFISH_2B)) ||
             !strncmp(salt, CRYPT_BLOWFISH_2X, strlen(CRYPT_BLOWFISH_2X)) ||
             !strncmp(salt, CRYPT_BLOWFISH_2Y, strlen(CRYPT_BLOWFISH_2Y)))
    {
        /* $2a$05$1234567890123456789012 */
        salt_len = 22 + 7;
    }
    else if (!strncmp(salt, CRYPT_SHA_256, strlen(CRYPT_SHA_256)) ||
             !strncmp(salt, CRYPT_SHA_512, strlen(CRYPT_SHA_512)))
    {
        sp = strrchr(salt, '$');
        salt_len = sp - salt + 1;
    }
    if(salt_len == 0)//locked user, user with nologin etc
    {
        st = -1;
        errno = EPERM;
        goto error;
    }
    salt[salt_len] = '\0';
    *ret_salt = salt;
    *ret_encpwd = encpwd;
    salt = NULL;

error:
    if (is_locked)
    {
        ulckpwdf();
        error = seteuid(cur_uid);
    }
    if (spbuf_str)
    {
        free(spbuf_str);
    }
    if (st == -1)
    {
        if (salt)
        {
            free(salt);
            salt = NULL;
        }
        if (encpwd)
        {
            free(encpwd);
            encpwd = NULL;
        }
    }
    return st;
}
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
				int argc, const char **argv)
{
	unsigned int ctrl, lctrl;
	int retval, i;
	int remember = -1;

	/* <DO NOT free() THESE> */
	const char *user;
	char *pass_old, *pass_new;
	/* </DO NOT free() THESE> */

	D(("called."));

#ifdef USE_LCKPWDF
	/* our current locking system requires that we lock the
	   entire password database.  This avoids both livelock
	   and deadlock. */
	/* These values for the number of attempts and the sleep time
	   are, of course, completely arbitrary.
	   My reading of the PAM docs is that, once pam_chauthtok() has been
	   called with PAM_UPDATE_AUTHTOK, we are obliged to take any
	   reasonable steps to make sure the token is updated; so retrying
	   for 1/10 sec. isn't overdoing it.
	   The other possibility is to call lckpwdf() on the first
	   pam_chauthtok() pass, and hold the lock until released in the
	   second pass--but is this guaranteed to work? -SRL */
	i=0;
	while((retval = lckpwdf()) != 0 && i < 100) {
		usleep(1000);
	}
	if(retval != 0) {
		return PAM_AUTHTOK_LOCK_BUSY;
	}
#endif
	ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);

	/*
	 * First get the name of a user
	 */
	retval = pam_get_user(pamh, &user, "Username: "******"bad username [%s]", user);
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return PAM_USER_UNKNOWN;
		}
		if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
			_log_err(LOG_DEBUG, pamh, "username [%s] obtained",
			         user);
	} else {
		if (on(UNIX_DEBUG, ctrl))
			_log_err(LOG_DEBUG, pamh,
			         "password - could not identify user");
#ifdef USE_LCKPWDF
		ulckpwdf();
#endif
		return retval;
	}

	D(("Got username of %s", user));

	/*
	 * This is not an AUTH module!
	 */
	if (on(UNIX__NONULL, ctrl))
		set(UNIX__NULLOK, ctrl);

	if (on(UNIX__PRELIM, ctrl)) {
		/*
		 * obtain and verify the current password (OLDAUTHTOK) for
		 * the user.
		 */
		char *Announce;

		D(("prelim check"));

		if (_unix_blankpasswd(ctrl, user)) {
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return PAM_SUCCESS;
		} else if (off(UNIX__IAMROOT, ctrl)) {

			/* instruct user what is happening */
#define greeting "Changing password for "
			Announce = (char *) malloc(sizeof(greeting) + strlen(user));
			if (Announce == NULL) {
				_log_err(LOG_CRIT, pamh,
				         "password - out of memory");
#ifdef USE_LCKPWDF
				ulckpwdf();
#endif
				return PAM_BUF_ERR;
			}
			(void) strcpy(Announce, greeting);
			(void) strcpy(Announce + sizeof(greeting) - 1, user);
#undef greeting

			lctrl = ctrl;
			set(UNIX__OLD_PASSWD, lctrl);
			retval = _unix_read_password(pamh, lctrl
						     ,Announce
					     ,"(current) UNIX password: "******"password - (old) token not obtained");
#ifdef USE_LCKPWDF
				ulckpwdf();
#endif
				return retval;
			}
			/* verify that this is the password for this user */

			retval = _unix_verify_password(pamh, user, pass_old, ctrl);
		} else {
			D(("process run by root so do nothing this time around"));
			pass_old = NULL;
			retval = PAM_SUCCESS;	/* root doesn't have too */
		}

		if (retval != PAM_SUCCESS) {
			D(("Authentication failed"));
			pass_old = NULL;
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return retval;
		}
		retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
		pass_old = NULL;
		if (retval != PAM_SUCCESS) {
			_log_err(LOG_CRIT, pamh,
			         "failed to set PAM_OLDAUTHTOK");
		}
		retval = _unix_verify_shadow(user, ctrl);
		if (retval == PAM_AUTHTOK_ERR) {
			if (off(UNIX__IAMROOT, ctrl))
				_make_remark(pamh, ctrl, PAM_ERROR_MSG,
					    "You must wait longer to change your password");
			else
				retval = PAM_SUCCESS;
		}
	} else if (on(UNIX__UPDATE, ctrl)) {
		/*
		 * tpass is used below to store the _pam_md() return; it
		 * should be _pam_delete()'d.
		 */

		char *tpass = NULL;
		int retry = 0;

		/*
		 * obtain the proposed password
		 */

		D(("do update"));

		/*
		 * get the old token back. NULL was ok only if root [at this
		 * point we assume that this has already been enforced on a
		 * previous call to this function].
		 */

		if (off(UNIX_NOT_SET_PASS, ctrl)) {
			retval = pam_get_item(pamh, PAM_OLDAUTHTOK
					      ,(const void **) &pass_old);
		} else {
			retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
					      ,(const void **) &pass_old);
			if (retval == PAM_NO_MODULE_DATA) {
				retval = PAM_SUCCESS;
				pass_old = NULL;
			}
		}
		D(("pass_old [%s]", pass_old));

		if (retval != PAM_SUCCESS) {
			_log_err(LOG_NOTICE, pamh, "user not authenticated");
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return retval;
		}
		retval = _unix_verify_shadow(user, ctrl);
		if (retval != PAM_SUCCESS) {
			_log_err(LOG_NOTICE, pamh, "user not authenticated 2");
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return retval;
		}
		D(("get new password now"));

		lctrl = ctrl;

		if (on(UNIX_USE_AUTHTOK, lctrl)) {
			set(UNIX_USE_FIRST_PASS, lctrl);
		}
		retry = 0;
		retval = PAM_AUTHTOK_ERR;
		while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
			/*
			 * use_authtok is to force the use of a previously entered
			 * password -- needed for pluggable password strength checking
			 */

			retval = _unix_read_password(pamh, lctrl
						     ,NULL
					     ,"Enter new UNIX password: "******"Retype new UNIX password: "******"password - new password not obtained");
				}
				pass_old = NULL;	/* tidy up */
#ifdef USE_LCKPWDF
				ulckpwdf();
#endif
				return retval;
			}
			D(("returned to _unix_chauthtok"));

			/*
			 * At this point we know who the user is and what they
			 * propose as their new password. Verify that the new
			 * password is acceptable.
			 */

			if (pass_new[0] == '\0') {	/* "\0" password = NULL */
				pass_new = NULL;
			}
			retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
		}

		if (retval != PAM_SUCCESS) {
			_log_err(LOG_NOTICE, pamh,
			         "new password not acceptable");
			_pam_overwrite(pass_new);
			_pam_overwrite(pass_old);
			pass_new = pass_old = NULL;	/* tidy up */
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return retval;
		}
		/*
		 * By reaching here we have approved the passwords and must now
		 * rebuild the password database file.
		 */

		/*
		 * First we encrypt the new password.
		 */

		if (on(UNIX_MD5_PASS, ctrl)) {
			tpass = crypt_md5_wrapper(pass_new);
		} else {
			/*
			 * Salt manipulation is stolen from Rick Faith's passwd
			 * program.  Sorry Rick :) -- alex
			 */

			time_t tm;
			char salt[3];

			time(&tm);
			salt[0] = bin_to_ascii(tm & 0x3f);
			salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
			salt[2] = '\0';

			if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
				/* 
				 * to avoid using the _extensions_ of the bigcrypt()
				 * function we truncate the newly entered password
				 */
				char *temp = malloc(9);
				char *e;

				if (temp == NULL) {
					_log_err(LOG_CRIT, pamh,
					         "out of memory for password");
					_pam_overwrite(pass_new);
					_pam_overwrite(pass_old);
					pass_new = pass_old = NULL;	/* tidy up */
#ifdef USE_LCKPWDF
					ulckpwdf();
#endif
					return PAM_BUF_ERR;
				}
				/* copy first 8 bytes of password */
				strncpy(temp, pass_new, 8);
				temp[8] = '\0';

				/* no longer need cleartext */
				e = bigcrypt(temp, salt);
				tpass = x_strdup(e);

				_pam_overwrite(e);
				_pam_delete(temp);	/* tidy up */
			} else {
				char *e;

				/* no longer need cleartext */
				e = bigcrypt(pass_new, salt);
				tpass = x_strdup(e);

				_pam_overwrite(e);
			}
		}

		D(("password processed"));

		/* update the password database(s) -- race conditions..? */

		retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
		                     remember);
		_pam_overwrite(pass_new);
		_pam_overwrite(pass_old);
		_pam_delete(tpass);
		pass_old = pass_new = NULL;
	} else {		/* something has broken with the module */
Exemple #13
0
int auth_change_pwd(char *user, char *newpwd)
{
	struct passwd *spw;
	struct spwd *stp;
	char hash[35] = "";
	long today;

	FILE *fd;

	if (0 != lckpwdf())
	{
		return 1;
	}

	/* open passwd */
	spw = getpwnam(user);

	if (spw == 0)
	{
		return 1;
	}

	if (g_strncmp(spw->pw_passwd, "x", 3) != 0)
	{
		/* old system with only passwd */
		if (auth_crypt_pwd(spw->pw_passwd, newpwd, hash) != 0)
		{
			ulckpwdf();
			return 1;
		}

		spw->pw_passwd = g_strdup(hash);
		fd = fopen("/etc/passwd", "rw");
		putpwent(spw, fd);
	}
	else
	{
		/* the system is using shadow */
		stp = getspnam(user);

		if (stp == 0)
		{
			return 1;
		}

		/* old system with only passwd */
		if (auth_crypt_pwd(stp->sp_pwdp, newpwd, hash) != 0)
		{
			ulckpwdf();
			return 1;
		}

		stp->sp_pwdp = g_strdup(hash);
		today = g_time1() / SECS_PER_DAY;
		stp->sp_lstchg = today;
		stp->sp_expire = today + stp->sp_max + stp->sp_inact;
		fd = fopen("/etc/shadow", "rw");
		putspent(stp, fd);
	}

	ulckpwdf();
	return 0;
}
Exemple #14
0
xmlNodePtr users_getxml(xmlNsPtr ns, char** msg)
{
	xmlNodePtr auth_node, user, aux_node;
	struct passwd *pwd;
	struct spwd *spwd;
	const char* value;
	char *path = NULL;

	if (!ncds_feature_isenabled("ietf-system", "local-users")) {
		return (NULL);
	}

	/* authentication */
	auth_node = xmlNewNode(ns, BAD_CAST "authentication");

	/* authentication/user-authentication-order */
	asprintf(&path, "/files/%s/PasswordAuthentication", NETOPEER_SSHD_CONF);
	aug_get(sysaugeas, path, &value);
	free(path);
	if (value != NULL && strcmp(value, "yes") == 0) {
		xmlNewChild(auth_node, auth_node->ns, BAD_CAST "user-authentication-order", BAD_CAST "local-users");
	}

	/* authentication/user[] */
	if (lckpwdf() != 0) {
		*msg = strdup("Failed to acquire shadow file lock.");
		xmlFreeNode(auth_node);
		return (NULL);
	}

	setpwent();

	while ((pwd = getpwent()) != NULL) {
		/* authentication/user */
		user = xmlNewChild(auth_node, auth_node->ns, BAD_CAST "user", NULL);

		/* authentication/user/name */
		xmlNewChild(user, user->ns, BAD_CAST "name", BAD_CAST pwd->pw_name);

		/* authentication/user/passwd */
		if (pwd->pw_passwd[0] == 'x') {
			/* get data from /etc/shadow */
			setspent();
			spwd = getspnam(pwd->pw_name);
			if (spwd != NULL && /* no record, wtf?!? */
					spwd->sp_pwdp[0] != '!' && /* account not initiated or locked */
					spwd->sp_pwdp[0] != '*') { /* login disabled */
				xmlNewChild(user, user->ns, BAD_CAST "password", BAD_CAST spwd->sp_pwdp);
			}
		} else if (pwd->pw_passwd[0] != '*') {
			/* password is stored in /etc/passwd or refers to something else (e.g., NIS server) */
			xmlNewChild(user, user->ns, BAD_CAST "password", BAD_CAST pwd->pw_passwd);
		} /* else password is disabled */

		/* authentication/user/authorized-key[] */
		if ((aux_node = authkey_getxml(pwd->pw_name, user->ns, msg)) != NULL) {
			xmlAddChildList(user, aux_node);
		} else {
			/* ignore failures in this case */
			free(*msg);
			*msg = NULL;
		}
	}

	endspent();
	endpwent();
	ulckpwdf();

	return (auth_node);
}
Exemple #15
0
static const char* set_passwd(const char *name, const char *passwd, char **msg)
{
	FILE *f = NULL;
	struct spwd *spwd, new_spwd;
	const char *en_passwd; /* encrypted password */
	struct stat st;

	assert(name);
	assert(passwd);

	/* check password format */
	if ((passwd[0] != '$') ||
			(passwd[1] != '0' && passwd[1] != '1' && passwd[1] != '5' && passwd[1] != '6') ||
			(passwd[2] != '$')) {
		asprintf(msg, "Wrong password format (user %s).", name);
		return (NULL);
	}

	if (passwd[1] == '0') {
		/* encrypt the password */
		get_login_defs();
		en_passwd = pw_encrypt(&(passwd[3]), crypt_make_salt(NULL, NULL));
	} else {
		en_passwd = passwd;
	}

	/*
	 * store encrypted password into shadow
	 */

	/* lock shadow file */
	if (lckpwdf() != 0) {
		*msg = strdup("Failed to acquire shadow file lock.");
		return (NULL);
	}
	/* init position in shadow */
	setspent();

	/* open new shadow */
	f = fopen(SHADOW_COPY, "w");
	if (f == NULL) {
		asprintf(msg, "Unable to prepare shadow copy (%s).", strerror(errno));
		endspent();
		ulckpwdf();
		return (NULL);
	}
	/* get file stat of the original file to make a nice copy of it */
	stat(SHADOW_ORIG, &st);
	fchmod(fileno(f), st.st_mode);
	fchown(fileno(f), st.st_uid, st.st_gid);

	while ((spwd = getspent()) != NULL) {
		if (strcmp(spwd->sp_namp, name) == 0) {
			/*
			 * we have the entry to change,
			 * make the copy, modifying the original
			 * structure doesn't seem as a good idea
			 */
			memcpy(&new_spwd, spwd, sizeof(struct spwd));
			new_spwd.sp_pwdp = (char*) en_passwd;
			spwd = &new_spwd;
		}
		/* store the record into the shadow copy */
		putspent(spwd, f);
	}
	endspent();
	fclose(f);

	if (rename(SHADOW_COPY, SHADOW_ORIG) == -1) {
		asprintf(msg, "Unable to rewrite shadow database (%s).", strerror(errno));
		unlink(SHADOW_COPY);
		ulckpwdf();
		return (NULL);
	}
	ulckpwdf();

	return (en_passwd);
}
Exemple #16
0
/*
 *  setpwnam () --
 *	takes a struct passwd in which every field is filled in and valid.
 *	If the given username exists in the passwd file, the entry is
 *	replaced with the given entry.
 */
int setpwnam(struct passwd *pwd)
{
	FILE *fp = NULL, *pwf = NULL;
	int save_errno;
	int found;
	int namelen;
	int buflen = 256;
	int contlen, rc;
	char *linebuf = NULL;
	char *tmpname = NULL;

	pw_init();

	if ((fp = xfmkstemp(&tmpname)) == NULL)
		return -1;

	/* ptmp should be owned by root.root or root.wheel */
	if (fchown(fileno(fp), (uid_t) 0, (gid_t) 0) < 0)
		goto fail;

	/* acquire exclusive lock */
	if (lckpwdf() < 0)
		goto fail;
	pwf = fopen(PASSWD_FILE, "r");
	if (!pwf)
		goto fail;

	namelen = strlen(pwd->pw_name);

	linebuf = malloc(buflen);
	if (!linebuf)
		goto fail;

	/* parse the passwd file */
	found = false;

	/* Do you wonder why I don't use getpwent? Read comments at top of
	 * file */
	while (fgets(linebuf, buflen, pwf) != NULL) {
		contlen = strlen(linebuf);
		while (linebuf[contlen - 1] != '\n' && !feof(pwf)) {
			char *tmp;
			/* Extend input buffer if it failed getting the whole line,
			 * so now we double the buffer size */
			buflen *= 2;
			tmp = realloc(linebuf, buflen);
			if (tmp == NULL)
				goto fail;
			linebuf = tmp;
			/* And fill the rest of the buffer */
			if (fgets(&linebuf[contlen], buflen / 2, pwf) == NULL)
				break;
			contlen = strlen(linebuf);
			/* That was a lot of work for nothing. Gimme perl! */
		}

		/* Is this the username we were sent to change? */
		if (!found && linebuf[namelen] == ':' &&
		    !strncmp(linebuf, pwd->pw_name, namelen)) {
			/* Yes! So go forth in the name of the Lord and
			 * change it!  */
			if (putpwent(pwd, fp) < 0)
				goto fail;
			found = true;
			continue;
		}
		/* Nothing in particular happened, copy input to output */
		fputs(linebuf, fp);
	}

	/* xfmkstemp is too restrictive by default for passwd file */
	if (fchmod(fileno(fp), 0644) < 0)
		goto fail;
	rc = close_stream(fp);
	fp = NULL;
	if (rc != 0)
		goto fail;

	fclose(pwf);	/* I don't think I want to know if this failed */
	pwf = NULL;

	if (!found) {
		errno = ENOENT;	/* give me something better */
		goto fail;
	}

	/* we don't care if we can't remove the backup file */
	unlink(PASSWD_FILE ".OLD");
	/* we don't care if we can't create the backup file */
	ignore_result(link(PASSWD_FILE, PASSWD_FILE ".OLD"));
	/* we DO care if we can't rename to the passwd file */
	if (rename(tmpname, PASSWD_FILE) < 0)
		goto fail;
	/* finally:  success */
	ulckpwdf();
	return 0;

 fail:
	save_errno = errno;
	ulckpwdf();
	if (fp != NULL)
		fclose(fp);
	if (tmpname != NULL)
		unlink(tmpname);
	free(tmpname);
	if (pwf != NULL)
		fclose(pwf);
	free(linebuf);
	errno = save_errno;
	return -1;
}
static bool ChangePasswordHashUsingLckpwdf(const char *puser, const char *password)
{
    bool result = false;

    struct stat statbuf;
    const char *passwd_file = "/etc/shadow";
    if (stat(passwd_file, &statbuf) == -1)
    {
        passwd_file = "/etc/passwd";
    }

    Log(LOG_LEVEL_VERBOSE, "Changing password hash for user '%s' by editing '%s'.", puser, passwd_file);

    if (lckpwdf() != 0)
    {
        Log(LOG_LEVEL_ERR, "Not able to obtain lock on password database.");
        return false;
    }

    char backup_file[strlen(passwd_file) + strlen(".cf-backup") + 1];
    xsnprintf(backup_file, sizeof(backup_file), "%s.cf-backup", passwd_file);
    unlink(backup_file);

    char edit_file[strlen(passwd_file) + strlen(".cf-edit") + 1];
    xsnprintf(edit_file, sizeof(edit_file), "%s.cf-edit", passwd_file);
    unlink(edit_file);

    if (!CopyRegularFileDisk(passwd_file, backup_file))
    {
        Log(LOG_LEVEL_ERR, "Could not back up existing password database '%s' to '%s'.", passwd_file, backup_file);
        goto unlock_passwd;
    }

    FILE *passwd_fd = fopen(passwd_file, "r");
    if (!passwd_fd)
    {
        Log(LOG_LEVEL_ERR, "Could not open password database '%s'. (fopen: '%s')", passwd_file, GetErrorStr());
        goto unlock_passwd;
    }
    int edit_fd_int = open(edit_file, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR);
    if (edit_fd_int < 0)
    {
        if (errno == EEXIST)
        {
            Log(LOG_LEVEL_CRIT, "Temporary file already existed when trying to open '%s'. (open: '%s') "
                "This should NEVER happen and could mean that someone is trying to break into your system!!",
                edit_file, GetErrorStr());
        }
        else
        {
            Log(LOG_LEVEL_ERR, "Could not open password database temporary file '%s'. (open: '%s')", edit_file, GetErrorStr());
        }
        goto close_passwd_fd;
    }
    FILE *edit_fd = fdopen(edit_fd_int, "w");
    if (!edit_fd)
    {
        Log(LOG_LEVEL_ERR, "Could not open password database temporary file '%s'. (fopen: '%s')", edit_file, GetErrorStr());
        close(edit_fd_int);
        goto close_passwd_fd;
    }

    while (true)
    {
        size_t line_size = 0;
        char *line = NULL;

        int read_result = CfReadLine(&line, &line_size, passwd_fd);
        if (read_result < 0)
        {
            if (!feof(passwd_fd))
            {
                Log(LOG_LEVEL_ERR, "Error while reading password database: %s", GetErrorStr());
                free(line);
                goto close_both;
            }
            else
            {
                break;
            }
        }

        // Editing the password database is risky business, so do as little parsing as possible.
        // Just enough to get the hash in there.
        char *field_start = NULL;
        char *field_end = NULL;
        field_start = strchr(line, ':');
        if (field_start)
        {
            field_end = strchr(field_start + 1, ':');
        }
        if (!field_start || !field_end)
        {
            Log(LOG_LEVEL_ERR, "Unexpected format found in password database while editing user '%s'. Not updating.",
                puser);
            free(line);
            goto close_both;
        }

        // Worst case length: Existing password is empty plus one '\n' and one '\0'.
        char new_line[strlen(line) + strlen(password) + 2];
        *field_start = '\0';
        *field_end = '\0';
        if (strcmp(line, puser) == 0)
        {
            xsnprintf(new_line, sizeof(new_line), "%s:%s:%s\n",
                     line, password, field_end + 1);
        }
        else
        {
            xsnprintf(new_line, sizeof(new_line), "%s:%s:%s\n",
                     line, field_start + 1, field_end + 1);
        }

        free(line);

        size_t new_line_size = strlen(new_line);
        size_t written_so_far = 0;
        while (written_so_far < new_line_size)
        {
            clearerr(edit_fd);
            size_t written = fwrite(new_line, 1, new_line_size, edit_fd);
            if (written == 0)
            {
                const char *err_str;
                if (ferror(edit_fd))
                {
                    err_str = GetErrorStr();
                }
                else
                {
                    err_str = "Unknown error";
                }
                Log(LOG_LEVEL_ERR, "Error while writing to file '%s'. (fwrite: '%s')", edit_file, err_str);
                goto close_both;
            }
            written_so_far += written;
        }
    }

    fclose(edit_fd);
    fclose(passwd_fd);

    if (!CopyFilePermissionsDisk(passwd_file, edit_file))
    {
        Log(LOG_LEVEL_ERR, "Could not copy permissions from '%s' to '%s'", passwd_file, edit_file);
        goto unlock_passwd;
    }

    if (rename(edit_file, passwd_file) < 0)
    {
        Log(LOG_LEVEL_ERR, "Could not replace '%s' with edited password database '%s'. (rename: '%s')",
            passwd_file, edit_file, GetErrorStr());
        goto unlock_passwd;
    }

    result = true;

    goto unlock_passwd;

close_both:
    fclose(edit_fd);
    unlink(edit_file);
close_passwd_fd:
    fclose(passwd_fd);
unlock_passwd:
    ulckpwdf();

    return result;
}
Exemple #18
0
int main(int argc, char **argv) {
  hardened_shadow_openlog("userdel");

  if (lckpwdf() != 0)
    err(EXIT_FAILURE, "lckpwdf");

  parse_args(argc, argv);

  bool user_private_groups;
  if (!hardened_shadow_config_get_bool("USER_PRIVATE_GROUPS",
                                       &user_private_groups)) {
    errx(EXIT_FAILURE, "failed to retrieve USER_PRIVATE_GROUPS setting");
  }

  const char *userdel_command = NULL;
  if (!hardened_shadow_config_get_path("USERDEL_COMMAND", &userdel_command))
    errx(EXIT_FAILURE, "failed to retrieve USERDEL_COMMAND setting");

  /* Note: shadow-utils try to detect whether user is logged in.
   * However, it is not obvious how to perform such detection reliably,
   * especially taking daemons like cron or at into account (they usually
   * don't go through PAM or anything that'd allow us to prevent
   * running processes as user being delete in a race-free way.
   *
   * Because of those drawbacks, no such checking is performed here.
   */

  {
    char* user_dir_path = NULL;
    if (asprintf(&user_dir_path, "/etc/hardened-shadow/%s", user_name) < 0)
      errx(EXIT_FAILURE, "memory allocation failure");
    if (!recursively_remove_path(user_dir_path))
      errx(EXIT_FAILURE, "failed to remove %s", user_dir_path);
    free(user_dir_path);
  }

  if (!hardened_shadow_update_group_change_user_name(user_name, NULL))
    errx(EXIT_FAILURE, "hardened_shadow_update_group_change_user_name failed");
  if (!hardened_shadow_replace_passwd(user_name, NULL))
    errx(EXIT_FAILURE, "hardened_shadow_replace_passwd failed");
  if (user_private_groups) {
    struct group *gr = getgrnam(user_name);
    if (gr) {
      gid_t private_gid = gr->gr_gid;

      bool found = false;
      struct passwd *pwd = NULL;
      setpwent();
      while ((pwd = getpwent())) {
        if (pwd->pw_gid == private_gid) {
          found = true;
          break;
        }
      }
      endpwent();

      if (found) {
        warnx("not removing private user group because it is a primary group "
              "of at least one other user");
      } else if (!hardened_shadow_replace_group(user_name, NULL)) {
        errx(EXIT_FAILURE, "failed to remove user private group");
      }
    }
  }

  /* Note: preserve exact wording of syslog messages from shadow-utils
   * where possible. */
  hardened_shadow_syslog(LOG_INFO, "delete user '%s'", user_name);

  if (flag_remove && !recursively_remove_path(user_home))
    errx(EXIT_FAILURE, "remove_home_directory failed");

  if (!run_userdel_command(userdel_command))
    errx(EXIT_FAILURE, "failed to run USERDEL_COMMAND");

  hardened_shadow_flush_nscd("passwd");
  hardened_shadow_flush_nscd("group");

  if (ulckpwdf() != 0)
    warn("ulckpwdf");

  free(user_home);

  return EXIT_SUCCESS;
}