Go to main content
Oracle® Solaris 11 セキュリティー開発者ガイド

印刷ビューの終了

更新: 2016 年 11 月
 
 

PAM サービスを提供するモジュールの記述

PAM サービスを呼び出すアプリケーションは、32 ビットまたは 64 ビットのバイナリとしてコンパイルできます。PAM モジュールは dlopen を介して共有オブジェクトとしてロードされるため、32 ビットと 64 ビットのどちらの形式のアプリケーションでも使用できるようにするには、両方のバージョンで提供する必要があります。PAM フレームワークがアプリケーションの適切なバージョンをロードできるように、32 ビットと 64 ビットの両バージョンのモジュールをインストールする方法については、『Kerberos およびその他の認証サービスの管理』ガイドの「PAM モジュールを追加する方法」を参照してください。

PAM サービスプロバイダの要件

PAM サービスモジュールは、pam_get_item(3PAM)pam_set_item(3PAM) を使ってアプリケーションとの通信を行います。サービスモジュール同士の通信には、pam_get_data(3PAM)pam_set_data(3PAM) が使用されます。同一プロジェクト内のサービスモジュール間でデータを交換する必要がある場合、そのプロジェクト内で一意に決まるデータ名が確立されます。その後、サービスモジュールは、関数 pam_get_data()pam_set_data() を使ってそのデータを共有できます。

    サービスモジュールは、次の 3 種類の PAM 戻りコードのいずれかを返す必要があります。

  • PAM_SUCCESS: 要求されているポリシーに合致しているという肯定的な決定を、モジュールが行なった場合。

  • PAM_IGNORE: モジュールがポリシー決定を行わなかった場合。

  • PAM_error: モジュールが参加していた決定が失敗した場合。error は、汎用エラーコード、サービスモジュールタイプに固有のコードのいずれかです。別のサービスモジュールタイプのエラーコードは使えません。エラーコードについては、pam_sm_module-typepam_authtok_get(5) のマニュアルページを参照してください。

1 つのサービスモジュール内に複数の機能が含まれている場合、それらの機能はそれぞれ個別のモジュールに分割することをお勧めします。そうすることで、システム管理者は、ポリシー構成時によりきめ細かい制御を行えるようになります。

    新しいサービスモジュールを作成した場合、対応するマニュアルページを提供する必要があります。マニュアルページには、次の情報を含める必要があります。

  • モジュールが受け入れる引数。

  • モジュールが実装しているすべての関数。

  • アルゴリズムに対するフラグの効果。

  • 必要とされるすべての PAM アイテム。

  • このモジュールに固有のエラー戻りコード。

サービスモジュールは、メッセージを抑制するための PAM_SILENT フラグを尊重することが求められます。デバッグ情報を syslog に記録するには、debug 引数を指定することをお勧めします。デバッグ情報を記録するには、syslog(3C) 使用時に LOG_AUTH と LOG_DEBUG を指定します。その他のメッセージは、LOG_AUTH と適切な優先度を指定して syslog() に送るべきです。openlog(3C)closelog(3C)、および setlogmask(3C) という 3 つの関数はアプリケーションの設定に悪影響を与えるので、決して使用しないでください。

PAM プロバイダサービスモジュールの例

ここでは、PAM サービスモジュールの例を示します。この例では、ユーザーがこのサービスへのアクセスが許可されているグループのメンバーかどうかを確認します。プロバイダは、成功の場合にアクセスを許可し、失敗の場合にエラーメッセージをログに記録します。

    この例では、次の手順を実行します。

  1. PAM 構成からこのモジュールに渡されたオプションを解析します。pam.conf(4) を参照してください。

    このモジュールは、nowarn オプションおよび debug オプション、さらに固有のオプション group を受け入れます。group オプションを使用する場合、デフォルトで使用されるグループ root 以外の特定のグループに対してアクセスを許可するようにモジュールを構成できます。この例については、ソースコードの DEFAULT_GROUP の定義を参照してください。グループ staff に属するユーザーのみにアクセスを制限するには、管理者が PAM サービスのアカウント構成に次のエントリを追加します。

    account required pam_members_only.so.1 group=staff
  2. ユーザー名、サービス名、およびホスト名を取得します。

    ユーザー名は、現在のユーザー名を PAM ハンドルから取り出す、pam_get_user(3PAM) の呼び出しによって取得されます。ユーザー名が設定されていない場合、アクセスは拒否されます。サービス名とホスト名は、pam_get_item(3PAM) の呼び出しによって取得されます。

  3. 有効にする情報を検証します。

    ユーザー名が設定されていない場合、アクセスは拒否されます。有効にするグループが定義されていない場合、アクセスは拒否されます。

  4. 現在のユーザーが、このホストへのアクセスが許可されている特殊グループのメンバーであることを確認し、アクセスを許可します。

    固有のグループが定義されていてもメンバーが 1 つも含まれていない場合は、このモジュールがどのアカウント検証プロセスにも参加しないことを示す、PAM_IGNORE が返されます。決定は、スタック上のほかのモジュールに委ねられます。

  5. ユーザーが特殊グループのメンバーではない場合は、アクセスが拒否されたことをユーザーに知らせるメッセージを表示します。

    メッセージをログに記録してこのイベントを記録します。

次の例は、PAM プロバイダ例のソースコードです。

使用例 6  PAM サービスモジュール例
/*
 * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
 */


#include <stdio.h>
#include <stdlib.h>
#include <grp.h>
#include <string.h>
#include <syslog.h>
#include <libintl.h>
#include <security/pam_appl.h>

/*
 * by default, only users who are a member of group "root" are allowed access
 */
#define	DEFAULT_GROUP "root"

static char *NOMSG =
        "Sorry, you are not on the access list for this host - access denied.";

int
pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, const char **argv) {
    char *user = NULL;
    char *host = NULL;
    char *service = NULL;
    const char *allowed_grp = DEFAULT_GROUP;
    char grp_buf[4096];
    struct group grp;
    struct pam_conv *conversation;
    struct pam_message message;
    struct pam_message *pmessage = &message;
    struct pam_response *res = NULL;
    int i;
    int nowarn = 0;
    int debug = 0;

    /* Set flags to display warnings if in debug mode. */
    for (i = 0; i < argc; i++) {
        if (strcasecmp(argv[i], "nowarn") == 0)
            nowarn = 1;
        else if (strcasecmp(argv[i], "debug") == 0)
            debug = 1;
        else if (strncmp(argv[i], "group=", 6) == 0)
            allowed_grp = &argv[i][6];
    }
    if (flags & PAM_SILENT)
        nowarn = 1;

    /* Get user name,service name, and host name. */
    (void) pam_get_user(pamh, &user, NULL);
    (void) pam_get_item(pamh, PAM_SERVICE, (void **) &service);
    (void) pam_get_item(pamh, PAM_RHOST, (void **) &host);

    /* Deny access if user is NULL. */
    if (user == NULL) {
        syslog(LOG_AUTH | LOG_DEBUG,
                "%s: members_only: user not set", service);
        return (PAM_USER_UNKNOWN);
    }

    if (host == NULL)
        host = "unknown";

    /*
     * Deny access if vuser group is required and user is not in vuser
     * group
     */
    if (getgrnam_r(allowed_grp, &grp, grp_buf, sizeof (grp_buf)) == NULL) {
        syslog(LOG_NOTICE | LOG_AUTH,
                "%s: members_only: group \"%s\" not defined",
                service, allowed_grp);
        return (PAM_SYSTEM_ERR);
    }

    /* Ignore this module if group contains no members. */
    if (grp.gr_mem[0] == 0) {
        if (debug)
            syslog(LOG_AUTH | LOG_DEBUG,
                "%s: members_only: group %s empty: "
                "all users allowed.", service, grp.gr_name);
        return (PAM_IGNORE);
    }

    /* Check to see if user is in group. If so, return SUCCESS. */
    for (; grp.gr_mem[0]; grp.gr_mem++) {
        if (strcmp(grp.gr_mem[0], user) == 0) {
            if (debug)
                syslog(LOG_AUTH | LOG_DEBUG,
                    "%s: user %s is member of group %s. "
                    "Access allowed.",
                    service, user, grp.gr_name);
            return (PAM_SUCCESS);
        }
    }

    /*
     * User is not a member of the group.
     * Set message style to error and specify denial message.
     */
    message.msg_style = PAM_ERROR_MSG;
    message.msg = gettext(NOMSG);

    /* Use conversation function to display denial message to user. */
    (void) pam_get_item(pamh, PAM_CONV, (void **) &conversation);
    if (nowarn == 0 && conversation != NULL) {
        int err;
        err = conversation->conv(1, &pmessage, &res,
                conversation->appdata_ptr);
        if (debug && err != PAM_SUCCESS)
            syslog(LOG_AUTH | LOG_DEBUG,
                "%s: members_only: conversation returned "
                "error %d (%s).", service, err,
                pam_strerror(pamh, err));

        /* free response (if any) */
        if (res != NULL) {
            if (res->resp)
                free(res->resp);
            free(res);
        }
    }

    /* Report denial to system log and return error to caller. */
    syslog(LOG_NOTICE | LOG_AUTH, "%s: members_only: "
            "Connection for %s not allowed from %s", service, user, host);

    return (PAM_PERM_DENIED);
}