Qtの基礎 - ユーザとグループ

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

ユーザ情報とグループ情報は、システムのセキュリティと権限管理の基盤となる重要な要素である。

Linuxにおけるユーザ情報とグループ情報の基本的な構造、保存場所、関連するファイル、管理コマンド等の情報は、
システム管理、セキュリティ設定、アプリケーション開発など、様々な場面で重要になる。

特に、ユーザ認証、アクセス制御、ファイル所有権の管理等のタスクを行う場合に、これらの概念の理解が不可欠である。


ユーザ情報

ユーザアカウント

各ユーザは、一意のユーザ名とユーザID (UID) を持つ。

UIDの値が0の場合は特別であり、rootユーザ (スーパーユーザ) に割り当てられる。
通常のユーザアカウントは、UIDの値が1000から始まる。(システムによって異なる場合がある)

/etc/passwdファイル

ユーザアカウント情報を格納する主要なファイルである。

各行が1つのユーザアカウントを表しており、以下に示す形式で情報が記録されている。

<ユーザ名>:<パスワード>:<UID>:<GID>:<GECOS>:<ホームディレクトリ>:<シェル>


  • ユーザ名
    ログイン名
  • パスワード
    暗号化されたパスワード (通常は'x'で、実際のパスワードは/etc/shadowに保存されている)
  • UID
    ユーザID
  • GID
    プライマリグループID
  • GECOS
    ユーザのフルネーム等の追加情報
  • ホームディレクトリ
    ユーザのホームディレクトリ
  • シェル
    ログイン時に使用されるシェル


/etc/shadowファイル

ユーザパスワードの暗号化された情報を格納する。

セキュリティ上の理由から、rootユーザのみがアクセス可能である。
パスワードの有効期限や変更履歴などの情報も含まれている。


グループ情報

グループ

ユーザをカテゴリ分けしており、ファイルやリソースへのアクセス権を管理するために使用される。
各グループは一意のグループ名とグループID (GID) を持つ。

/etc/groupファイル

グループ情報を格納する主要なファイルである。

各行が1つのグループを表しており、以下に示す形式で情報が記録されている。

<グループ名>:<パスワード>:<GID>:<ユーザリスト>


  • グループ名
    グループ名が表示されている。
  • パスワード
    グループパスワード (通常は使用されず、'x'が表示されている)
  • GID
    グループID
  • ユーザリスト
    グループに所属する追加ユーザのリスト (カンマ区切り)


プライマリグループとセカンダリグループ

プライマリグループとは、ユーザが作成したファイルのデフォルトグループ所有権である。
セカンダリグループとは、ユーザが追加で所属するグループである。


関連コマンドとツール

  • useradd
  • usermod
  • userdel
    ユーザアカウントの作成、変更、削除
  • groupadd
  • groupmod
  • groupdel
    グループの作成、変更、削除
  • passwd
    ユーザパスワードの変更
  • chage
    ユーザパスワードの有効期限設定
  • id
    ユーザのUID、GID、所属グループの表示
  • groups
    ユーザの所属グループの表示
  • whoami
    現在のユーザ名の表示



セキュリティ

  • 最小権限の原則
    ユーザには必要最小限の権限のみを付与
  • rootアカウントの使用制限
    日常的なタスクには一般ユーザアカウントを使用
  • 定期的なパスワード変更とパスワードポリシーの実施
  • 未使用アカウントの定期的な監査と削除
  • sudoの適切な設定と使用



プログラムでの使用

C/C++において、ユーザ情報およびグループ情報にアクセスする主な関数を、以下に示す。

  • getpwnam関数
  • getpwuid関数
    ユーザ情報の取得
  • getgrnam関数
  • getgrgid関数
    グループ情報の取得
  • getpwent関数
  • getgrent関数
    全てのユーザ情報およびグループ情報の順次取得


上記の関数を使用する場合は、適切なエラーハンドリングとメモリ管理が重要である。


Linuxのユーザの取得

ユーザ名およびホームディレクトリの取得 : Qtライブラリの使用

以下の例では、Linuxのユーザ名とホームディレクトリを取得している。

  • ユーザ名の取得
    環境変数USERを使用してユーザ名を取得する。
    環境変数USERが空の場合、代替として環境変数USERNAMEを取得する。(一部のシステムでは異なる環境変数を使用する可能性があるため)
  • ホームディレクトリの取得
    QDir::homePathメソッドを使用して、現在のユーザのホームディレクトリを取得する。


 #include <QCoreApplication>
 #include <QDir>
 #include <QProcessEnvironment>
 #include <QDebug>
 
 int main(int argc, char *argv[])
 {
    QCoreApplication a(argc, argv);
 
    // 現在ログインしているユーザ名を取得
    QString userName = qgetenv("USER");
    if (userName.isEmpty())
       userName = qgetenv("USERNAME");
 
    // 現在ログインしているユーザのホームディレクトリを取得
    QString homeDir = QDir::homePath();
 
    qDebug() << "Current user name: " << userName;
    qDebug() << "Home directory: " << homeDir;
 
    return a.exec();
 }


ユーザ情報の取得 : libcライブラリの使用 (推奨)

libcライブラリのgetpwent関数を使用することにより、システムの標準APIを使用してユーザ情報を取得できる。
これは、/etc/passwdファイルを直接読み込むよりも安全であり、かつ、異なるUNIXシステム間での互換性も高くなる。

このアプローチは安全で信頼性が高く、システムのセキュリティポリシーに準拠している。
また、/etc/passwdファイルへの直接アクセスを避けることにより、ファイルパーミッションの問題も回避することができる。

  1. libcライブラリのgetpwent関数、setpwent関数、endpwent関数の標準C関数を使用して、システムのパスワードデータベースにアクセスする。
  2. ファイルを直接読み込む代わりにシステムAPIを使用するため、より安全で信頼性が高くなる。
  3. 異なるUNIXシステム間での互換性が向上する。
  4. 文字エンコーディングの問題を避けるため、QString::fromLocal8Bitメソッドを使用して、システムのローカルエンコーディングからQStringクラスに変換している。


また、libCryptライブラリをリンクすることにより、一部のシステムで必要となる暗号化関連の関数にアクセスすることができる。

 # Qtプロジェクトファイル (.pro) を使用する場合
 
 ## libCryptライブラリを使用する場合
 ## libCryptライブラリを使用しない場合は、以下に示す設定は不要
 
 # Pkg-configを使用する場合
 CONFIG += link_pkgconfig
 PKGCONFIG += libcrypt
 
 # Pkg-configを使用しない場合
 LIBS += -lcrypt


 # CMakeを使用する場合 (CMakeLists.txtファイル)
 
 ## libCryptライブラリを使用する場合
 ## libCryptライブラリを使用しない場合は、以下に示す設定は不要
 find_package(PkgConfig REQUIRED)
 pkg_check_modules(LIBCRYPT REQUIRED libcrypt)
 
 target_link_libraries(${PROJECT_NAME} PRIVATE
    # ...略
    ${LIBCRYPT_LIBRARIES}
 )
 
 target_include_directories(${PROJECT_NAME} PRIVATE
    ${LIBCRYPT_INCLUDE_DIRS}
 )
 
 target_compile_options(${PROJECT_NAME} PRIVATE
    ${LIBCRYPT_CFLAGS_OTHER}
 )


 #include <QCoreApplication>
 #include <pwd.h>
 #include <sys/types.h>
 #include <QDebug>
 
 strut UserInfo {
    QString username;
    QString uid;
    QString gid;
    QString fullName;
    QString homeDir;
    QString shell;
 };
 
 QList<UserInfo> getUsers()
 {
    QList<UserInfo> users;
    struct passwd *pw;
 
    // passwdデータベースの先頭に巻き戻す
    setpwent();
 
    while ((pw = getpwent()) != nullptr) {
       UserInfo user;
       user.username = QString::fromLocal8Bit(pw->pw_name);
       user.uid      = QString::number(pw->pw_uid);
       user.gid      = QString::number(pw->pw_gid);
       user.fullName = QString::fromLocal8Bit(pw->pw_gecos);
       user.homeDir  = QString::fromLocal8Bit(pw->pw_dir);
       user.shell    = QString::fromLocal8Bit(pw->pw_shell);
       users.append(user);
    }
 
    // passwdデータベースを閉じる
    endpwent();
 
    return users;
 }
 
 int main(int argc, char *argv[])
 {
    QCoreApplication a(argc, argv);
 
    QList<UserInfo> users = getUsers();
 
    for (const auto &user : users) {
       qDebug() << "Username: " << user.username;
       qDebug() << "UID: " << user.uid;
       qDebug() << "GID: " << user.gid;
       qDebug() << "Full Name: " << user.fullName;
       qDebug() << "Home Directory: " << user.homeDir;
       qDebug() << "Shell: " << user.shell;
    }
 
    return a.exec();
 }


ユーザ情報の取得 : /etc/passwdファイルを読み込む (非推奨)

以下の例では、/etc/passwdファイルを読み込み、ユーザ情報を取得している。

ただし、/etc/passwdファイルの読み込むには、root権限が必要となる。
そのため、セキュリティ上の理由からより安全な方法 (例: getpwent関数の使用) を使用することを推奨する。

 #include <QCoreApplication>
 #include <QFile>
 #include <QTextStream>
 #include <QStringList>
 #include <QDebug>
 
 struct UserInfo {
    QString username;
    QString uid;
    QString gid;
    QString fullName;
    QString homeDir;
    QString shell;
 };
 
 QList<UserInfo> getUsers()
 {
    QList<UserInfo> users;
    QFile file("/etc/passwd");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
       qDebug() << "Failed to open /etc/passwd";
       return users;
    }
 
    QTextStream in(&file);
    while (!in.atEnd()) {
       QString line = in.readLine();
       QStringList fields = line.split(':');
       if (fields.size() >= 7) {
          UserInfo user;
          user.username = fields[0];
          user.uid = fields[2];
          user.gid = fields[3];
          user.fullName = fields[4];
          user.homeDir = fields[5];
          user.shell = fields[6];
          users.append(user);
       }
    }
 
    file.close();
 
    return users;
 }
 
 int main(int argc, char *argv[])
 {
    QCoreApplication a(argc, argv);
 
    QList<UserInfo> users = getUsers();
 
    for (const auto &user : users) {
       qDebug() << "Username: " << user.username;
       qDebug() << "UID: " << user.uid;
       qDebug() << "GID: " << user.gid;
       qDebug() << "Full Name: " << user.fullName;
       qDebug() << "Home Directory: " << user.homeDir;
       qDebug() << "Shell: " << user.shell;
    }
 
    return a.exec();
 }



Linuxのグループの取得

以下の例では、現在のユーザが所属しているグループ名を取得している。

ただし、この例はUNIX系システム (Linux、MacOS) 向けであるため、Windowsの場合は異なるアプローチが必要になる。
また、libcのシステムヘッダファイル (grp.h、pwd.h、unistd.h、sys/types.h) を使用しているため、クロスプラットフォームの互換性はない。

このサンプルコードは、以下に示すような手順で動作する。

  1. まず、現在のユーザのUIDを取得する。
  2. UIDを使用してユーザ情報を取得する。
  3. libcライブラリのgetgrouplist関数を使用して、ユーザが所属するグループの数とIDを取得する。
  4. 各グループIDに対して、libcライブラリのgetgrgid関数を使用してグループ名を取得する。
  5. 取得したグループ名をQStringListクラスに追加する。


エラーメッセージの生成時において、QString::fromLocal8Bitメソッドを使用することにより、
システムのローカルエンコーディングから正しくQString型に変換することができる。

QVectorクラスを使用することにより、Qtの提供する暗黙的な共有 (implicit sharing) の恩恵を受けられる可能性がある。
これは、特定の状況下でのパフォーマンス向上につながる可能性がある。

 // 使用方法
 
 QString errorMessage = "";
 QStringList userGroups = getUserGroups(errorMessage);
 
 if (!errorMessage.isEmpty()) {
    qDebug() << "Error occurred:" << errorMessage;
 }
 else if (userGroups.isEmpty()) {
    qDebug() << "User is not a member of any groups";
 }
 else {
    qDebug() << "User groups:" << userGroups;
 }


 #include <QCoreApplication>
 #include <QDebug>
 #include <grp.h>
 #include <pwd.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <errno.h>
 
 QStringList getUserGroups(QString &errorMessage)
 {
    QStringList groups;
    errorMessage.clear();
 
    uid_t uid = getuid();
 
    struct passwd *pw = getpwuid(uid);
    if (!pw) {
       errorMessage = QString("Failed to get user info: %1").arg(QString::fromLocal8Bit(strerror(errno)));
       return groups;
    }
 
    int ngroups = 0;
    if (getgrouplist(pw->pw_name, pw->pw_gid, nullptr, &ngroups) == -1) {
       errorMessage = "Failed to get group list size";
       return groups;
    }
 
    if (ngroups > 0) {
       QVector<gid_t> gids(ngroups);
       if (getgrouplist(pw->pw_name, pw->pw_gid, gids.data(), &ngroups) == -1) {
          errorMessage = "Failed to get group list";
          return groups;
       }
 
       for (int i = 0; i < ngroups; i++) {
          errno = 0;
          struct group *gr = getgrgid(gids[i]);
          if (gr) {
             groups.append(QString::fromLocal8Bit(gr->gr_name));
          }
          else {
             qWarning() << "Failed to get group name for gid" << gids[i] << ":" << QString::fromLocal8Bit(strerror(errno));
          }
       }
    }
    else {
       errorMessage = "User is not a member of any groups";
    }
 
    return groups;
 }