Open9
systemd-homedソースコードを読む
大まかな機能については調査済み。
これに関してどう実装されているのかを読み解いていく。
v248
コンポーネントは3つ
- homectl(1): フロントエンド実装
- homed-homed(8): バックエンド実装
- homework: 非同期処理実装
とりあえずユーザー新規作成ロジックを見る(ローカル&LUKS使う感じで)
homectl(https://github.com/systemd/systemd/blob/v248/src/home/homectl.c)を読む
- (L3329)エントリーポイント
- (L3337)argをparseして
create_home
呼び出し - (L1048)
create_home()
- (L1053)systemd bus(D-Bus)取得
r = acquire_bus(&bus);
- (L1057)polkit開始
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
- (L1078)ユーザー名をjsonに書き込み(
json.c
)r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
- (L110)password作成(パスワード入れるように促すダイアログもここ)
r = acquire_new_password(hr->user_name, hr, /* suggest = */ true, &new_password);
- (L1134)D-Bus経由で
homed
にCreateHome
メッセージを渡すr = bus_message_new_method_call(bus, &m, bus_mgr, "CreateHome");
- (残りはD-Busのタイムアウト等なので省略)
- (L1053)systemd bus(D-Bus)取得
homectlの仕事ははここまで。
homed(https://github.com/systemd/systemd/blob/v248/src/home/homed.c)を読む
- homedはdaemonとして存在しているためエントリーポイントは今回は見ない。homectlから送信された
CreateHome
は https://github.com/systemd/systemd/blob/v248/src/home/homed-manager-bus.c にて紐付けを行う。SD_BUS_METHOD_WITH_NAMES("CreateHome", "s", SD_BUS_PARAM(user_record), NULL,, method_create_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
- 紐付けられた
method_create_home()
- polkitに権限問い合わせ
r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.home1.create-home", NULL, true, UID_INVALID, &m->polkit_registry, error);
-
CAP_SYS_ADMIN
の指定により管理者権限が必要であることのチェックを行っている(-> Capability) - polkitの定義(
src/home/org.freedesktop.home1.policy
)このうち<action id="org.freedesktop.home1.create-home"> <description gettext-domain="systemd">Create a home area</description> <message gettext-domain="systemd">Authentication is required to create a user's home area.</message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> <allow_active>auth_admin_keep</allow_active> </defaults> </action>
- id: D-Busコマンド
- message: ユーザーに通知されるメッセージ(下記参照)
$ homectl create test1 🔐 Please enter new password for user test1: ***** 🔐 Please enter new password for user test1 (repeat): ***** ==== AUTHENTICATING FOR org.freedesktop.home1.create-home ==== Authentication is required to create a user's home area. <= これ Authenticating as: kc5m Password: polkit-agent-helper-1: pam_authenticate failed: Permission denied ==== AUTHENTICATION FAILED ==== Operation on home test1 failed: Access denied
- allow_any: 全般的な設定
- allow_inactive: not tty(pty等)なときに適用される設定
- allow_active: ttyな時に適用される設定
- auth_admin_keep: 管理者ユーザーとしての認証が必要 & sudoのように認証は数分間しか効力を持たない
-
- 対象ユーザーの作成
r = validate_and_allocate_home(m, hr, &h, error);
-
home_create()
を実行r = home_create(h, hr, error);
- polkitに権限問い合わせ
home_create
は別ファイル(src/home/homed-home.c
)
-
home_create()
でhomeworkの開始r = home_start_work(h, "create", h->record, secret);
-
home_start_work()
-> fork & execr = safe_fork_full("(sd-homework)", (int[]) { stdin_fd, stdout_fd }, 2, FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_REOPEN_LOG, &pid); // ... execl(homework, homework, verb, NULL);
Homework(src/home/homework.c
)を読む
エントリーポイントは同ファイルのrun()
-
run()
// ... else if (streq(argv[1], "create")) r = home_create(home, &new_home); // ...
-
home_create()
- 暗号化用にパスワード生成
r = user_record_compile_effective_passwords(h, &cache, &effective_passwords);
- 各種userhomeの種類に基づく処理にルーティング
switch (user_record_storage(h)) { case USER_LUKS: r = home_create_luks(h, &cache, effective_passwords, &new_home); break; case USER_DIRECTORY: case USER_SUBVOLUME: r = home_create_directory_or_subvolume(h, &new_home); break; case USER_FSCRYPT: r = home_create_fscrypt(h, effective_passwords, &new_home); break; case USER_CIFS: r = home_create_cifs(h, &new_home); break;
- 今回はLUKSの処理を置いたいので
home_create_luks()
を追う
- 暗号化用にパスワード生成
LUKS用の処理は/src/home/homework-luks.c
にて実装されている
-
home_create_luks()
- 場所を特定(ip=image path)
assert_se(ip = user_record_image_path(h));
- filesystemを特定(btrfs, fallback=ext4)
fstype = user_record_file_system_type(h);
- filesystemの利用可否を特定
r = mkfs_exists(fstype);
- partition/luks/fsのuuidの生成
r = sd_id128_randomize(&partition_uuid); r = sd_id128_randomize(&luks_uuid); r = sd_id128_randomize(&fs_uuid);
- dm(device mapper)の名前の生成(例:
/dev/mapper/home-test1
)r = make_dm_names(h->user_name, &dm_name, &dm_node);
- dmに対してテストアクセス
r = access(dm_node, F_OK);
- ipが
- 実block device
- block deviceを
open(2)
image_fd = open(ip, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
-
access(2)
を使用してsysfsに対象mappingが存在しないことを確認if (asprintf(&sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)) < 0) return log_oom(); if (access(sysfs, F_OK) < 0) { if (errno != ENOENT) return log_error_errno(errno, "Failed to check whether %s exists: %m", sysfs);
-
ioctl(2)
を使用してdiscard属性をonにする(TRIM/シンプロビジョニング対応)if (ioctl(image_fd, BLKDISCARD, (uint64_t[]){ 0, block_device_size }) < 0)
- block deviceを
- ファイルパス
- 親pathを取得
parent = dirname_malloc(ip);
- 親pathを作成
r = mkdir_p(parent, 0755);
- ファイルパスが存在するfilesystemのサイズを計算
r = calculate_disk_size(h, parent, &host_size);
- 親path下に一時ファイル名を生成
r = tempfn_random(ip, "homework", &temporary_image_path);
- 一時ファイルを
open(2)
image_fd = open( temporary_image_path, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW, 0600);
-
chattr(2)
でcow(Copy-On-Write)をoffにする(btrfs用)r = chattr_fd(image_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
- fileをtruncate(
fallocate(2)
&ftruncate(2)
)r = home_truncate(h, image_fd, temporary_image_path, host_size);
- discardをoffにする
- 親pathを取得
- 実block device
- 場所を特定(ip=image path)
- fdに対してpartition作成
r = make_partition_table( image_fd, user_record_user_name_and_realm(h), partition_uuid, &partition_offset, &partition_size, &disk_uuid);
- fdに対してloop device作成(=>
src/shared/loop-util.c
)r = loop_device_make(image_fd, O_RDWR, partition_offset, partition_size, 0, &loop);
- もしfdが
- loopback device
- (基本的に)その他へ
/* Oh! This is a loopback device? That's interesting! */
- block device
- block deviceをloopback deviceと見做し、受け取ったfdをポインタへ
- その他(file path)
-
losetup -f
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); nr = ioctl(control, LOOP_CTL_GET_FREE);
- loop deviceのfdを取得してポインタへ
loop = open(loopdev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags); loop_with_fd = TAKE_FD(loop)
-
- loopback device
- もしfdが
- loopを
flock(2)
r = loop_device_flock(loop, LOCK_EX);
- loopをLUKSフォーマット
r = luks_format( loop->node, dm_name, luks_uuid, user_record_user_name_and_realm(h), cache, effective_passwords, user_record_luks_discard(h) || user_record_luks_offline_discard(h), h, &cd);
- fsを作成
r = make_filesystem( dm_node, fstype, user_record_user_name_and_realm(h), fs_uuid, user_record_luks_discard(h));
- 作成したimage fileを
unshare(2)
(=> namespace)でmount(2)
r = home_unshare_and_mount(dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h)); // src/home/homework-mount.c へ
- (可能なら)btrfsのsub volume機能を使う
r = btrfs_subvol_make_fallback(subdir, 0700);
- mountしたファイルを
open(2)
root_fd = open(subdir, O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
- home directoryの初期化
r = home_populate(h, root_fd); // in home_populate() // スケルトンのコピー r = copy_skel(dir_fd, user_record_skeleton_directory(h)); // .identityの作成 r = home_store_embedded_identity(h, dir_fd, h->uid, NULL); // chown r = chown_recursive_directory(dir_fd, h->uid); // chmod r = change_access_mode(dir_fd, user_record_access_mode(h));
- sync
r = home_sync_and_statfs(root_fd, &sfs);
- (必要なら)trim
r = run_fitrim(root_fd);
- mountしたファイルを
close(2)
&umount(2)
root_fd = safe_close(root_fd); r = umount_verbose(LOG_ERR, "/run/systemd/user-home-mount", UMOUNT_NOFOLLOW);
- deviceをdeactivate(暗号化)
r = crypt_deactivate(cd, dm_name);
- 再読込
if (disk_uuid_path) // block deviceの場合(`BLKRRPART` = re-read partition table) (void) ioctl(image_fd, BLKRRPART, 0); else { // file systemの場合 r = fsync_directory_of_file(image_fd); }
- image fileを
close(2)
image_fd = safe_close(image_fd);
- 一時ファイル名から正規のファイル名に
rename(2)
if (rename(temporary_image_path, ip) < 0) {
- おわり
log_info("Everything completed."); print_size_summary(host_size, encrypted_size, &sfs);
ログインしてからの処理のメモ
-
src/home/pam_systemd_home.c
pam_sm_open_session()
acquire_home()
-
src/shared/bus-locator.c
bus_message_new_method_call()
-
src/home/homed-manager-bus.c
sd_bus_vtable()
bus_message_new_method_call()
method_acquire_home()
- (
generic_home_method()
)
-
src/home/homed-home-bus.c
bus_home_method_acquire()
-
src/home/home-d-home.c
home_schedule_operation()
home_schedule_operation()
-
src/sd-event.c
- (
sd_event_source_set_enabled()
)
- (
-
src/home/homed-home.c
on_pending()
home_dispatch_acquire()
home_activate_internal()
home_start_work()
execl()
-
src/home/homework
run()
home_activate()
-
src/home/homework-luks.c
-
home_activate_luks()
ここで復号化処理を行う(ツリーへ)home_prepare_luks()
-
luks_setup()
ここで cryptsetupのAPI(crypt_activate_by_volume_key()
)を呼び出して復号化
-
-
src/home/homework-mount.c
home_move_mount()
-
src/shared/mount-util.h
mount_nofollow_verbose()
-
src/shared/mount-util.c
mount_verbose_full()
mount()