ユーザーデータベースは、ログイン名と数値のユーザー ID をアカウントレコードにマップします。/etc/passwd はよく知られた表現ですが、アプリケーションはそのファイルが権威あるソースであると仮定してはなりません。glibc は Name Service Switch (NSS) を通じて passwd データベースを解決します。/etc/nsswitch.conf の passwd: エントリは、ローカルファイル、systemd、LDAP、NIS、その他のインストール済みプロバイダーを参照できます。
PAM はこれらの検索を実行しません。PAM は認証とアカウント管理ポリシーを処理し、NSS は ID データベースのクエリに応答します。
アカウントレコード #
<pwd.h> は公開レコードを定義します。
struct passwd {
char *pw_name; /* login name */
char *pw_passwd; /* password field, commonly "x" */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* primary group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* login shell */
};歴史的なパスワードフィールドは、通常の Linux インストールでは再利用可能なパスワードハッシュを公開しません。ハッシュは別途保護されたシャドウデータベースに保持され、公開 passwd レコードには通常 x または別のマーカーが含まれます。
ユーザーを検索する #
キー付き検索を行う関数は 2 つあります。
#include <pwd.h>
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);getpwnam() はログイン名で検索し、getpwuid() は数値 UID で検索します。どちらも成功時にライブラリ管理ストレージへのポインターを返します。後続のパスワードデータベース呼び出しでそのストレージが上書きされる可能性があり、アプリケーションは解放してはなりません。NULL は「見つからない」またはエラーのいずれかを意味するため、区別が必要な場合は呼び出し前に errno をゼロに設定し、後で確認します。
NSS が検索の意味を定義します。要求によってローカルファイルアクセス、IPC、ネットワーク通信が発生する可能性があり、NSS ポリシーは各プロバイダーの結果の後で停止または継続できます。したがって、/etc/passwd を直接解析することは、これらのインターフェイスを呼び出すことと同等ではありません。
再入可能インターフェイスを使用する #
マルチスレッドコードでは、文字列を呼び出し元が提供するストレージに配置する getpwnam_r() と getpwuid_r() を使用すべきです。
struct passwd pwd;
struct passwd *result;
char *buf = malloc(buflen);
int error = getpwnam_r("build", &pwd, buf, buflen, &result);
if (error != 0) {
/* error is an error number; ERANGE means the buffer was too small */
} else if (result == NULL) {
/* no matching account */
} else {
printf("%s has UID %ju\n", pwd.pw_name, (uintmax_t)pwd.pw_uid);
}検索が完了すると、見つからない場合も含めて関数は 0 を返します。*result は成功時に提供された struct passwd を指し、一致するレコードがない場合は NULL です。正のエラー番号は、戻り値 -1 を介さずに失敗を直接報告します。文字バッファーが参照されるすべての文字列を保持できない場合、関数は ERANGE を返します。バッファーを増やして再試行してください。sysconf() の _SC_GETPW_R_SIZE_MAX は初期サイズのヒントであり、-1 を返した場合に保証される上限ではありません。
pwd 内の戻りポインターは buf 内を参照するため、レコードを使用する間は両方のオブジェクトを存続させる必要があります。再入可能性はライブラリストレージを保護しますが、NSS プロバイダーの外部データベースを不変にしたり、個別の検索シーケンスをアトミックにしたりするものではありません。
データベースを列挙する #
ストリーム形式の API は、設定されたプロバイダーが公開する順序でアカウントレコードを訪問します。
setpwent();
struct passwd *entry;
while ((entry = getpwent()) != NULL)
printf("%s\n", entry->pw_name);
endpwent();setpwent()は列挙を巻き戻し、プロバイダーのリソースを開く場合があります。getpwent()は共有内部ストレージを使用して次のエントリを返します。endpwent()は列挙リソースを閉じます。
これらの関数はプロセス全体の列挙状態を保持し、複数スレッドによる同時使用に安全ではありません。getpwent_r() は呼び出し元が所有するレコードと文字列バッファーを提供しますが、列挙には共有ストリーム状態が残ります。別のスレッドがその状態を操作している場合、glibc は setpwent()、getpwent_r()、endpwent() を安全でないものとして分類します。他のスレッドが同時に列挙する可能性がある場合は、完全な列挙をシリアライズしてください。
ネットワーク接続されたディレクトリでは、列挙が不完全または高コストになることもあります。アプリケーションが 1 つの ID を必要とする場合はキー付き検索を使用し、完全スキャンを安定したスナップショットとして扱わないでください。
コメント
GitHub で閲覧・コメントする(投稿にはログインが必要です)