我需要根据 spwd.db 和 pwd.db 生成 master.passwd,就像使用 一样pwd_mkdb -p
。但是,我需要指定输入和输出路径。
因此,给定somesuch/spwd.db
和somesuch/pwd.db
,我想要生成somesuch/master.passwd
。
我天真地尝试了一些类似的事情,但pwd_mkdb -p -d somesuch/ somesuch/spwd.db
没有成功(“文件类型或格式不合适”)。
答案1
您无法使用 pwd_mkdb 生成master.passwd
。spwd.db
该-p
选项仅生成一个/etc/passwd
文件,该文件不是包含密码哈希值!
正确的做法是从备份中恢复master.passwd
。如果你没有备份,这是一个很好的例子,说明为什么你应该备份所有配置数据!
检查/var/backups
的备份副本master.passwd
。
如果找不到备份,则必须master.passwd
从 的内容中重建spwd.db
。文件/etc/pwd.db
和/etc/spwd.db
是 Berkeley 数据库文件:
file /etc/pwd.db
/etc/pwd.db: Berkeley DB 1.85 (Hash, version 2, native byte-order)
如果您想读取这些数据库,您可以使用db4_dump185
来自的程序/usr/ports/databases/db4
。
以这种方式读取文件时,请注意源代码中的以下信息/usr/src/usr.sbin/pwd_mkdb/pwd_mkdb.c
:
/*
* The databases actually contain three copies of the original data.
* Each password file entry is converted into a rough approximation
* of a ``struct passwd'', with the strings placed inline. This
* object is then stored as the data for three separate keys. The
* first key * is the pw_name field prepended by the _PW_KEYBYNAME
* character. The second key is the pw_uid field prepended by the
* _PW_KEYBYUID character. The third key is the line number in the
* original file prepended by the _PW_KEYBYNUM character. (The special
* characters are prepended to ensure that the keys do not collide.)
*/
阅读上述源代码,了解行如何存储在散列数据库中:
#define COMPACT(e) t = e; while ((*p++ = *t++));
#define SCALAR(e) store = htonl((uint32_t)(e)); \
memmove(p, &store, sizeof(store)); \
p += sizeof(store);
#define LSCALAR(e) store = HTOL((uint32_t)(e)); \
memmove(p, &store, sizeof(store)); \
p += sizeof(store);
#define HTOL(e) (openinfo.lorder == BYTE_ORDER ? \
(uint32_t)(e) : \
bswap32((uint32_t)(e)))
和
/* Create secure data. */
p = sbuf;
COMPACT(pwd.pw_name);
COMPACT(pwd.pw_passwd);
SCALAR(pwd.pw_uid);
SCALAR(pwd.pw_gid);
SCALAR(pwd.pw_change);
COMPACT(pwd.pw_class);
COMPACT(pwd.pw_gecos);
COMPACT(pwd.pw_dir);
COMPACT(pwd.pw_shell);
SCALAR(pwd.pw_expire);
SCALAR(pwd.pw_fields);
sdata.size = p - sbuf;
答案2
因为我确实想审计一个系统以确认文件/etc/spwd.db
是否如其/etc/master.passwd
所述,所以我写了以下内容。我怀疑它并不完整,但对我的情况来说已经足够了。如果您认为缺少了某些内容 - 让分支default:
发出switch
一些调试信息 - 它至少会抱怨我忽略的版本条目。
祝你好运。
#include <stdio.h>
#include <sys/types.h>
#include <limits.h>
#include <db.h>
#include <fcntl.h>
#include <sys/endian.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <pwd.h>
#define _PW_VERSION_MASK '\xF0'
#define _PW_VERSIONED(x, v) ((unsigned char)(((x) & 0xCF) | ((v)<<4)))
#define _PW_KEYBYNAME '\x31' /* stored by name */
#define _PW_KEYBYNUM '\x32' /* stored by entry in the "file" */
#define _PW_KEYBYUID '\x33' /* stored by uid */
#define _PW_KEYYPENABLED '\x34' /* YP is enabled */
#define _PW_KEYYPBYNUM '\x35' /* special +@netgroup entries */
#define _NPW_KEYBYNAME '\x41' /* stored by name */
#define _NPW_KEYBYNUM '\x42' /* stored by entry in the "file" */
#define _NPW_KEYBYUID '\x43' /* stored by uid */
#define _NPW_KEYYPENABLED '\x44' /* YP is enabled */
#define _NPW_KEYYPBYNUM '\x45' /* special +@netgroup entries */
#define _PWD_VERSION_KEY "\xFF" "VERSION"
static HASHINFO openinfo = {
4096, /* bsize */
32, /* ffactor */
256, /* nelem */
2048 * 1024, /* cachesize */
NULL, /* hash() */
BIG_ENDIAN /* lorder */
};
void die(char *reason) {
printf("FATAL: %s\n", reason? reason: strerror(errno));
exit(1);
}
#define UNCOMPACT(field, name) \
plen = strlen(p); \
if (i + plen > len) die("data malformed"); \
field = p; \
i += plen + 1; \
p += plen + 1
#define UNSCALAR(field) \
if (i + sizeof(uint32_t) > len) die("data malformed"); \
field = (*unscalar_func)(*(uint32_t *)p); \
i += sizeof(uint32_t); \
p += sizeof(uint32_t)
uint32_t uint32_t_noop(uint32_t data) {
return data;
}
void print_pwd(uint32_t (*unscalar_func)(uint32_t), size_t len, char *data) {
struct passwd pwd;
size_t plen, i = 0;
char *p;
p = data;
UNCOMPACT(pwd.pw_name, "pwd.pw_name");
UNCOMPACT(pwd.pw_passwd, "pwd.pw_passwd");
UNSCALAR(pwd.pw_uid);
UNSCALAR(pwd.pw_gid);
UNSCALAR(pwd.pw_change);
UNCOMPACT(pwd.pw_class, "pwd.pw_class");
UNCOMPACT(pwd.pw_gecos, "pwd.pw_gecos");
UNCOMPACT(pwd.pw_dir, "pwd.pw_dir");
UNCOMPACT(pwd.pw_shell, "pwd.pw_shell");
UNSCALAR(pwd.pw_expire);
UNSCALAR(pwd.pw_fields);
printf("%s:%s:" "%d:%d:" "%s:" "%ld:%ld:" "%s:%s:%s\n",
pwd.pw_name, pwd.pw_passwd,
pwd.pw_uid, pwd.pw_gid,
pwd.pw_class,
pwd.pw_change, pwd.pw_expire,
pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell);
}
int main(int argc, char *argv[]) {
DB *db;
DBT key, data;
int rc;
db = dbopen(argv[1], O_RDONLY, 0777, DB_HASH, &openinfo);
for (;;) {
rc = db->seq(db, &key, &data, R_NEXT);
if (rc == -1) die("R_NEXT");
if (rc == 1) break;
// Overdone - letting sort -u reduce this later.
switch (*(char *)key.data) {
case _PW_KEYBYNAME:
case _PW_KEYBYNUM:
case _PW_KEYBYUID:
print_pwd(uint32_t_noop, data.size, (char *)data.data);
break; ;;
case _NPW_KEYBYNAME:
case _NPW_KEYBYNUM:
case _NPW_KEYBYUID:
print_pwd(ntohl, data.size, (char *)data.data);
break; ;;
default:
break; ;;
}
}
}