Техническое примечание TN2228

Выполнение при входе в систему

Этот technote объясняет, как можно записать код, координирующийся с процессом входа в систему. Это полезно при многих обстоятельствах. Например, если Вы разрабатываете продукт для управляемых установок, сбрасывающий предпочтения пользователя к некоторому известному состоянию, Вы хотите, чтобы Ваш код бежал за корневым каталогом пользователя, смонтирован, но прежде чем работали любые пользовательские процессы. Исторически такие продукты, возможно, использовали рычаг входа в систему, но метод, описанный в этом technote (плагин авторизации), имеет много важных преимуществ перед рычагом входа в систему.

Этот technote предназначен для разработчиков Mac OS X, продукты которых должны быть скоординированы с процессом входа в систему GUI.

Введение
Близко, но никакая сигара
Сделайте правильную вещь
Опасность Уилл Робинсон
Дополнительные материалы для чтения
История версии документа

Введение

При некоторых обстоятельствах необходимо выполнить код, поскольку пользователь входит в систему, и гарантировать, что могут продолжаться выполнения кода к завершению перед входом в систему. Например, если Вы разрабатываете административный продукт, хотящий сбросить предпочтения пользователя к определенному состоянию, необходимо сделать это после того, как корневой каталог смонтирован (иначе, продукт не работал бы на пользователей FileVault или на пользователей с сетевыми корневыми каталогами), но прежде чем любые приложения имели возможность выполнить и считать те предпочтения.

Этот technote описывает рекомендуемый подход для выполнения кода, это координируется с процессом входа в систему. Это запускается с обзора методов, использовавшихся для этого в прошлом, но существенно испорченных (Близко, Но Никакая Сигара). Это тогда описывает рекомендуемый метод, а именно, создавая плагин авторизации (Сделайте Правильную Вещь). Наконец, это завершает обсуждением некоторых глюков, связанных с этим методом (Опасность Уилл Робинсон).

Когда плагин авторизации не является надлежащим, важно понять. Два общих падежа:

Близко, но никакая сигара

Исторически разработчики использовали много методов для выполнения кода во время входа в систему, и большинство из них является несоответствующим по различным причинам. В этом разделе описываются те методы и их проблемы.

Рычаг входа в систему

Рычаг входа в систему документируется в Системные Темы Программирования Запуска. Это имеет много недостатков и обычно считается осуждаемым. В частности:

  • Существует только один рычаг входа в систему, что означает, что это не подходит для разработчиков обертки уменьшения, потому что Вам нельзя гарантировать это, пользователь уже не установил некоторое другое программное обеспечение, это использует его. В целом необходимо полагать, что рычаг входа в систему резервируется для использования администраторами сайта.

  • Рычаг входа в систему достигнут в одно определенное время во время процесса входа в систему (после того, как корневой каталог был смонтирован), который не достаточно гибок для покрытия всех обстоятельств.

Демон

Различные разработчики попытались сделать этот вид вещи от демона (launchd демон или элемент запуска). Существует много проблем с использованием демона для этого вида вещи.

  • Это подвергается условиям состязания. Запуск Mac OS X является очень асинхронным. Таким образом это возможно (хотя несколько вряд ли), что пользователь мог бы войти в систему, прежде чем Ваш демон был даже запущен.

  • Нет никакой гарантии, что демон может получить доступ к корневому каталогу (специально для FileVault и сетевых пользователей корневого каталога), который делает их неподходящими для определенных задач.

Посмотрите Техническое примечание TN2083, 'Демоны и Агенты' для получения дополнительной информации о демонах.

Агент

Различные разработчики попытались использовать агент (launchd агент, элемент входа в систему или глобальный элемент входа в систему), чтобы сделать этот вид вещи. Этот метод является неподходящим по ряду причин.

  • Это подвергается условиям состязания. Агенты запускаются после входа в систему параллельно с другими элементами их класса. Таким образом нет никакого способа быть уверенным, что изменения, внесенные агентом, будут влиять на его коллеги.

  • Агенты всегда бегут за корневым каталогом, был смонтирован, который делает их неподходящими для определенных задач.

Посмотрите Техническое примечание TN2083, 'Демоны и Агенты' для получения дополнительной информации об агентах.

Сделайте правильную вещь

Рекомендуемый подход для выполнения кода, это координируется с процессом входа в систему, должен создать плагин авторизации.

Когда пользователь входит в систему, loginwindow попытки получить system.login.console право авторизации. Если Вы смотрите на правильное определение в базе данных авторизации (в настоящее время /etc/authorization), Вы будете видеть что-то как Перечисление 1. В частности, mechanisms массив содержит список строк механизма, связанных с этим правом авторизации.

Перечисление 1  консольная авторизация входа в систему прямо на Mac OS X 10.5

<key>system.login.console</key>
<dict>
    <key>class</key>
    <string>evaluate-mechanisms</string>
    <key>comment</key>
    <string>Login mechanism based rule. Not for general use, yet.</string>
    <key>mechanisms</key>
    <array>
        <string>builtin:smartcard-sniffer,privileged</string>
        <string>loginwindow:login</string>
        <string>builtin:reset-password,privileged</string>
        <string>builtin:auto-login,privileged</string>
        <string>builtin:authenticate,privileged</string>
        <string>HomeDirMechanism:login,privileged</string>
        <string>HomeDirMechanism:status</string>
        <string>MCXMechanism:login</string>
        <string>loginwindow:success</string>
        <string>loginwindow:done</string>
    </array>
</dict>

Когда loginwindow попытки получить system.login.console право, Authorization Services выполняет каждый из механизмов, описанных строками в массиве, поочередно. Каждая строка включает имя плагина авторизации (перед двоеточием) и имя механизма, реализованного тем плагином (после двоеточия). Это дополнительно заканчивается», дал полномочия», чтобы указать, что механизм должен быть выполнен как корень (больше об этом в Проблемах Контекста).

Возможно записать плагин авторизации, реализующий пользовательский механизм, и затем добавьте имя того механизма к этому массиву и вызовите его во время входа в систему. Это происходит и для ручного и для автоматического имени для входа в систему.

Позиция Вашего механизма в массиве определяет, когда это будет выполнено. Правильная спецификация по умолчанию для system.login.console списки один механизм (loginwindow:login) это представляет пользовательский интерфейс окна входа в систему и другого (HomeDirMechanism:login,privileged) это монтирует корневой каталог пользователя (если, скажем, это - сетевой корневой каталог, или на образе диска FileVault). При установке пользовательского механизма необходимо поместить его так, чтобы он работал в надлежащей точке относительно этих стандартных механизмов. Позиция, которую Вы выбираете, зависит от природы Вашего продукта. Например:

Начало работы

Лучшее место для запуска при разработке плагина авторизации является Примером кода 'NullAuthPlugin'. Захватите выборку и начните настраивать!

Остаток от этого раздела описывает некоторые проблемы, с которыми Вы могли бы встретиться при создании плагина.

Всегда монтируйте обезьяну царапины

Самый простой способ избежать быть укушенным этим состоит в том, чтобы включить имя для входа в систему SSH на Вашей машине (Удаленный вход в систему в панели Sharing Установок системы). Тот путь, если что-нибудь идет не так, как надо, можно зарегистрировать на пути SSH, чтобы отладить и исправить его.

Если Вы сталкиваетесь с проблемами, и Вы не включили SSH (или у Вас нет доступа к другой машине для использования в качестве клиента SSH), можно фиксировать вещи путем начальной загрузки машины в однопользовательском режиме.

Независимо, я строго рекомендую сделать резервное копирование базы данных авторизации прежде, чем выполнить любую работу с плагинами авторизации. Перечисление 2 показывает, как сделать это на существующих системах.

Перечисление 2  Поддержка и восстановление базы данных авторизации

$ # Backup the authorization database $ sudo cp /etc/authorization /etc/authorization-orig $ # Restore from the backup $ sudo cp /etc/authorization-orig /etc/authorization

Быстрое переключение между пользователями

Во многих случаях Вы не должны перезапускать машину для тестирования плагина авторизации. Вместо этого можно включить быстрое переключение между пользователями (в панели Account Установок системы) и переключиться непосредственно на окно входа в систему. Оттуда, войдите в систему как различный пользователь. Ваши механизмы плагина авторизации будут вызваны почти таким же способом, как они были бы во время запуска.

Отладка

Механика отладки плагина авторизации может быть сложной. Например:

  • Вам определенно будут нужны две машины, потому что плагин авторизации работает во время входа в систему, что означает, что нет никакого способа использовать отладчик на машине, выполняющей плагин.

  • Возможно использовать средство отладки XCode с двумя машинами для отладки плагина в XCode IDE. Посмотрите Техническое примечание TN2108, 'Отладив Плагин Авторизации С XCode' для инструкций о том, как сделать это.

  • Во многих случаях, однако, это проще только к SSH в машину и строку команды выполнения GDB.

  • Помните, что Ваш код является плагином, это размещается в системном процессе (см. Проблемы Контекста для подробных данных), и таким образом необходимо будет выполнить GDB как корень (обычно использующий sudo).

Можно также добавить обильное журналирование к плагину и отладить его путем посылки сообщения-розыгрыша через журналы. Пример кода 'NullAuthPlugin' показывает, как сделать тот системный журнал использования; более современный пример использовал бы ASL. Для примера того, как использовать ASL, посмотрите 'Выбранный' Пример кода.

Установка Вашего плагина

На Mac OS X 10.5 и позже, сторонние плагины авторизации должны быть установлены в /Library/Security/SecurityAgentPlugins. В более ранних системах они должны быть установлены в /System/Library/CoreServices/SecurityAgentPlugins. Файлы и каталоги, составляющие плагин, должны принадлежать root с группой владения wheel, и они не должны быть перезаписываемы никем кроме корня.

Как только Вы установили свой плагин, необходимо активировать его путем изменения базы данных авторизации. Во время разработки можно сделать это путем редактирования файла базы данных авторизации (в настоящее время /etc/authorization) непосредственно. Однако для широкого развертывания масштаба необходимо изменить базу данных с помощью APIs, предоставленного Концепцией безопасности (<Security/AuthorizationDB.h>).

Перечисление 3 показывает пример того, как сделать это. Подпрограмма верхнего уровня, AddMechanismToConsoleLoginRight, добавит механизм к mechanisms массив system.login.console право авторизации, или прежде или после HomeDirMechanism.

Перечисление 3  , Активирующее плагин авторизации

#include <assert.h>
#include <CoreServices/CoreServices.h>
#include <Security/Security.h>

static void InsertMechanismRelativeToHomeDirMechanism(
    CFMutableArrayRef   mechanisms,
    CFStringRef         mechanismStr,
    Boolean             beforeHomeDirMechanism
)
    // Adds the mechanism specified mechanismStr to the mechanisms array. 
    // If beforeHomeDirMechanism is true, mechanismStr is added immediately 
    // before the first instance of "HomeDirMechanism"; otherwise it is added 
    // after the last instance.
{
    CFIndex         mechanismCount;
    CFIndex         mechanismIndex;
    CFIndex         insertionIndex;
    Boolean         isHomeDirMechanism;
    CFStringRef     mechanism;

    assert(mechanisms != NULL);
    assert(mechanismStr != NULL);

    mechanismCount = CFArrayGetCount(mechanisms);
    insertionIndex = mechanismCount; // add after last entry by default
    for (mechanismIndex = 0; mechanismIndex < mechanismCount; mechanismIndex++) {
        mechanism = CFArrayGetValueAtIndex(mechanisms, mechanismIndex);
        isHomeDirMechanism = (
               (mechanism != NULL) 
            && (CFGetTypeID(mechanism) == CFStringGetTypeID()) 
            && CFStringHasPrefix(mechanism, CFSTR("HomeDirMechanism:"))
        );
        if (isHomeDirMechanism) {
            if (beforeHomeDirMechanism) {
                insertionIndex = mechanismIndex;
                break;
            } else {
                insertionIndex = mechanismIndex + 1;
            }
        }
    }
    CFArrayInsertValueAtIndex(mechanisms, insertionIndex, mechanismStr);
}

static OSStatus AddMechanismToConsoleLoginRight(
    AuthorizationRef    authRef,
    CFStringRef         authPluginName,
    CFStringRef         mechanismID,
    Boolean             privileged,
    Boolean             beforeHomeDirMechanism
)
    // Adds the mechanism specified authPluginName and mechanismID to the 
    // "mechanisms" array of the "system.login.console" right definition. 
    // If privileged is true, the mechanism runs as root. If 
    // beforeHomeDirMechanism is true, mechanismStr is added immediately 
    // before the first instance of "HomeDirMechanism"; otherwise it is added 
    // after the last instance.
{
    OSStatus                err;
    CFStringRef             mechanismStr;
    CFDictionaryRef         rightDict;
    CFStringRef             authClass;
    CFArrayRef              authMechanisms;
    CFMutableArrayRef       newMechanisms;
    CFMutableDictionaryRef  newRightDict;
    static const char *     kConsoleLoginRightName = "system.login.console";

    assert(authRef != NULL);
    assert(authPluginName != NULL);
    assert(mechanismID != NULL);

    mechanismStr = NULL;
    rightDict = NULL;
    newRightDict = NULL;
    newMechanisms = NULL;

    // Construct a correctly formatted mechanism string.

    err = noErr;
    mechanismStr = CFStringCreateWithFormat(
        NULL, 
        NULL, 
        CFSTR("%@:%@%@"), 
        authPluginName, 
        mechanismID, 
        (privileged ? CFSTR(",privileged") : CFSTR(""))
    );
    if (mechanismStr == NULL) {
        err = coreFoundationUnknownErr;
    }

    // Get the right definition and check the class is "evaluate-mechanisms".

    if (err == noErr) {
        err = AuthorizationRightGet(kConsoleLoginRightName, &rightDict);
    }
    if (err == noErr) {
        authClass = (CFStringRef) CFDictionaryGetValue(rightDict, CFSTR("class"));
        if ( (authClass == NULL) 
          || (CFGetTypeID(authClass) != CFStringGetTypeID()) ) {
            err = coreFoundationUnknownErr;
        } else if ( ! CFEqual(authClass, CFSTR("evaluate-mechanisms")) ) {
            err = errAuthorizationInternal;
        }
    }

    // Get the mechanisms array and check whether our mechanism is already present.

    if (err == noErr) {
        authMechanisms = (CFArrayRef) CFDictionaryGetValue(
            rightDict, 
            CFSTR("mechanisms")
        );
        if ( (authMechanisms == NULL) 
          || (CFGetTypeID(authMechanisms) != CFArrayGetTypeID()) ) {
            err = coreFoundationUnknownErr;
        }
    }
    if ( (err == noErr) 
      && ! CFArrayContainsValue(
               authMechanisms, 
               CFRangeMake(0, CFArrayGetCount(authMechanisms)), 
               mechanismStr
           ) ) {

        // If it's not, add our mechanism and write back the right definition.

        newMechanisms = CFArrayCreateMutableCopy(NULL, 0, authMechanisms);
        if (newMechanisms == NULL) {
            err = coreFoundationUnknownErr;
        }
        if (err == noErr) {
            InsertMechanismRelativeToHomeDirMechanism(
                newMechanisms, 
                mechanismStr, 
                beforeHomeDirMechanism
            );

            newRightDict = CFDictionaryCreateMutableCopy(NULL, 0, rightDict);
            if (newRightDict == NULL) {
                err = coreFoundationUnknownErr;
            }
            if (err == noErr) {
                CFDictionarySetValue(
                    newRightDict, 
                    CFSTR("mechanisms"), 
                    newMechanisms
                );

                err = AuthorizationRightSet(
                    authRef, 
                    kConsoleLoginRightName, 
                    newRightDict, 
                    NULL, 
                    NULL, 
                    NULL
                );
            }
        }
    }

    // Clean up.

    if (newRightDict != NULL) {
        CFRelease(newRightDict);
    }
    if (newMechanisms != NULL) {
        CFRelease(newMechanisms);
    }
    if (rightDict != NULL) {
        CFRelease(rightDict);
    }
    if (mechanismStr != NULL) {
        CFRelease(mechanismStr);
    }

    return err;
}

Вы не должны перезапускать систему для него для распознавания нового плагина авторизации или изменений в базе данных авторизации.

Проблемы контекста

Механизмы авторизации работают в очень необычном контексте, определенные значения которого зависят от того, устанавливаете ли Вы свои механизмы, как привилегированный или нет. Таблица 1 является кратким обзором контекста, наследованного каждым типом механизма авторизации.

Табличный 1  контекст Механизма авторизации

Привилегированный

UI ХОРОШО?

EUID / RUID [1]

Пространство имен начальной загрузки

Процесс [2]

да

нет

0/0

посмотрите ниже [3]

authorizationhost

нет

да

92/92

посмотрите ниже [3]

SecurityAgent

Примечания:

  1. Эффективный идентификатор пользователя и реальный пользователь ID, соответственно.

  2. Эта информация предоставлена для отладки целей только. Фактический процесс раньше работал, механизмы авторизации нельзя считать частью плагина авторизации API.

  3. Механизмы авторизации должны быть очень тщательными при контакте с пространствами имен начальной загрузки Маха. Это обсуждено более подробно в Проблемах Пространства имен Начальной загрузки Маха.

  4. UID 92 _securityagent. Это - специальный пользователь, которого ID раньше выполнял SecurityAgent процесс, и таким образом пользовательский интерфейс окна входа в систему.

Во многих случаях может быть необходимо для Вашего плагина реализовать пару сотрудничающих механизмов авторизации, тот, работающий привилегированный и тот, работающий непривилегированный. Например, если Вы пишете плагин авторизации, полностью сбрасывающий корневой каталог пользователя на входе в систему, Вам, возможно, понадобятся два механизма:

  • Привилегированный механизм для фактического изменения корневого каталога пользователя.

  • Непривилегированный механизм для отображения информации о прогрессе.

Это вызвано тем, что привилегированный механизм не может вывести на экран UI, и непривилегированный механизм работает как UID 92, и таким образом не может изменить корневой каталог пользователя.

Если необходимо создать два или больше механизма авторизации, можно использовать авторизацию вспомогательная информация для передачи между ними, как описано в следующем разделе.

Авторизация вспомогательная информация

Authorization Services поддерживает два словаря вспомогательной информации, которую она передает от одного плагина до следующего. Это контекст и подсказки, обсужденные подробно в Ссылке Плагина Авторизации.

Существует два общего использования для этой вспомогательной информации:

  1. При создании двух сотрудничающих механизмов можно передать информацию между ними посредством подсказок авторизации. Т.е. первый механизм, который выполнения могут депонировать информацию в подсказках, которые будут взяты вторым механизмом.

  2. Если Вам нужна информация о пользователе, в настоящее время входящем в систему, можно получить ее от контекста авторизации.

Как пример точки 2 при разработке плагина авторизации, сбрасывающего предпочтения пользователя, необходимо будет знать идентификатор пользователя пользователя, входящего в систему (таким образом, можно переключиться на того пользователя при записи в файловую систему), и путь к их корневому каталогу. Можно получить их от «uid» и «разместить» значения контекста, соответственно. Перечисление 4 показывает, как можно получить значение «uid» от контекста авторизации.

Перечисление 4  , Получающее доступ к 'uid' значению контекста

static uid_t GetUIDFromContext(
    const AuthorizationCallbacks *  authServerFuncs, 
    AuthorizationEngineRef          engine
)
    // Returns the "uid" value from the authorization context, or 
    // -2 (nobody) if the value is not present or can't be fetched. 
    // authServerFuncs is the value passed to your plug-in's 
    // AuthorizationPluginCreate routine. engine is the value 
    // passed to your MechanismCreate routine.
{
    OSStatus                    err;
    uid_t                       result;
    AuthorizationContextFlags   junkFlags;
    const AuthorizationValue *  value;

    assert(authServerFuncs != NULL);
    assert(engine != NULL);

    result = (uid_t) -2;
    err = authServerFuncs->GetContextValue(engine, "uid", &junkFlags, &value);
    if ( (err == noErr) && (value->length == sizeof(uid_t)) ) {
        result = * (const uid_t *) value->data;
    } else {
        // [... log the failure ...]
    }

    return result;
}

Опасность Уилл Робинсон

Следующие разделы описывают много неочевидных ловушек, связанных с созданием плагина авторизации.

Предоставление доступа корневого каталога

Если Вы пишете механизм авторизации, хотящий получить доступ к корневому каталогу пользователя, необходимо рассмотреть полномочия файловой системы. В частности непривилегированные механизмы авторизации работают как эффективный идентификатор пользователя (EUID) 92 (_securityagent). Стандартная проверка полномочий будет обычно препятствовать тому, чтобы они получили доступ к корневому каталогу пользователя.

С другой стороны, привилегированные механизмы авторизации работают как EUID 0 (root). Таким образом Вы могли бы думать, что такие механизмы могут всегда получать доступ к корневому каталогу пользователя (предполагающий, что они бегут HomeDirMechanism), однако, это не имеет место. Большинство сетевых файловых систем обрабатывает корневые доступы, как будто они были доступами пользователем nobody. Таким образом, если у пользователя есть сетевой корневой каталог, привилегированный механизм авторизации должен будет установить свой EUID в того из пользователя, входящего в систему перед способностью получить доступ к корневому каталогу пользователя.

Можно получить идентификатор пользователя пользователя, входящего в систему от контекста авторизации (см. Авторизацию Вспомогательная информация). Можно изменить использование EUID pthread_setugid_np как обсуждено в Быть Хорошим К Вашему Узлу.

Предотвращение доступа корневого каталога

Если Вы пишете механизм авторизации, работающий прежде HomeDirMechanism (т.е. прежде чем корневой каталог пользователя, как гарантируют, будет смонтирован), необходимо бояться пытаться получить доступ к их корневому каталогу. Это может быть хитро, особенно в ситуациях, где необходимо переключить EUID на UID входящего в систему пользователя, потому что некоторые общие основы получают доступ к корневому каталогу неочевидными способами.

Платформа, вызывающая большинство проблем в этом отношении, является Базовой Основой. Базовая Основа пытается получить доступ к корневому каталогу пользователя для определения их текстового кодирования по умолчанию (сохраненный в файле ~/.CFUserTextEncoding). Если Вы переключаете EUID на UID входящего в систему пользователя и затем вызываете CF, у Вас могут быть проблемы, когда Базовая Основа получает доступ к этому файлу. Можно предотвратить этот доступ путем установки переменной окружения, говорящей Базовой Основе текстовое кодирование по умолчанию для использования. Имя переменной окружения __CF_USER_TEXT_ENCODING. Его значение должно быть создано со строкой формата «0x%X:0:0», где %X заменяется UID входящего в систему пользователя.

Для максимизации шанса продолжающейся совместимости на уровне двоичных кодов необходимо попытаться ограничить себя низкоуровневыми платформами. В частности платформы, которые безопасны от демона, должны быть более или менее безопасными в контексте. С другой стороны, платформы, которые не безопасны от демона, почти наверняка собираются вызвать проблемы.

Посмотрите Техническое примечание TN2083, 'Демоны и Агенты' для списка безопасных от демона платформ.

Проблемы пространства имен начальной загрузки Маха

Ваш механизм авторизации наследовал очень необычное пространство имен начальной загрузки Маха. Пространство имен в конечном счете предназначено для становления GUI, на сеанс загружают пространство имен для пользователя, входящего в систему. Однако, в то время как вход в систему происходит, доступность основанных на Махе служб может варьироваться неочевидными способами.

Таблица 2 показывает, как можно получить доступ к службам Маха, зарегистрированным в определенных пространствах имен на Mac OS X 10.5 и позже.

Таблица 2  , Получающая доступ к службам Маха от механизма авторизации на Mac OS X 10.5

Пространство имен начальной загрузки Маха

Как получить доступ

глобальная переменная

всегда доступный

в расчете на пользователя

см. [1] ниже

предварительный вход в систему

всегда доступный

GUI на сеанс

никогда доступный

не-GUI на сеанс

никогда доступный

Примечания:

  1. Получить доступ к службам в расчете на пользователя (как кэш учетных данных Kerberos, edu.mit.Kerberos.CCacheServer) необходимо переключить EUID на тот из UID входящего в систему пользователя. Это имеет много важных последствий.

    • Ваш механизм должен работать привилегированный, иначе он будет работать как EUID 92 (_securityagent) и будьте неспособны переключить его EUID.

    • Ваш механизм должен бежать за «uid» значением контекста, был установлен (см. Авторизацию Вспомогательная информация). На существующих системах это означает, что должно бежать builtin:authenticate,privileged механизм. Если Вы будете сразу работать прежде или сразу после, это не будет проблемой HomeDirMechanism.

Таблица 3 показывает ту же информацию для Mac OS X 10.4.x.

Таблица 3  , Получающая доступ к службам Маха от механизма авторизации на Mac OS X 10.4.x

Пространство имен [1] начальной загрузки Маха

Как получить доступ

глобальная переменная

всегда доступный

предварительно войдите в систему / GUI на сеанс

см. [2] ниже

не-GUI на сеанс

никогда доступный

Примечания:

  1. Mac OS X 10.4.x не поддерживает в расчете на пользователя или не-GUI пространства имен на сеанс.

  2. На Mac OS X 10.4.x нет никакого ясного перехода между контекстом перед входом в систему и GUI контекста на сеанс. Ваш механизм авторизации наследовал ссылку на пространство имен, которое является первоначально пространством имен перед входом в систему и затем, как только вход в систему завершен, становится GUI пространство имен на сеанс. Однако большая часть GUI на сеансовые службы не регистрируется, пока авторизация не завершена (и таким образом, не доступны Вашему механизму).

Будьте Хороши к своему узлу

Ваш плагин авторизации загружается и выполняется в системном процессе (см. Проблемы Контекста для подробных данных). Необходимо попытаться быть максимально хорошими к хост-процессу. В частности:

  • Попытайтесь избежать изменять значения всего процесса. Например, при изменении текущего рабочего каталога, Вы могли бы вызвать проблемы для других потоков, работающих в том же процессе. Если существует доступная альтернатива на поток, используйте ее!

  • Как заключение к вышеупомянутому, попытайтесь избежать полагаться на любые значения всего процесса.

  • Когда Вы сделаны, если необходимо изменить значение всего процесса, возвратить его. Также попытайтесь ограничить объем кода, выполняющийся, в то время как изменяется значение.

  • Не используйте чрезмерные ресурсы. Например, не выделяйте так много памяти, что хост-процесс исчерпывает виртуальное адресное пространство.

  • Если Ваш сменный Objective C использования, добавляет префикс к Вашим именам классов для предотвращения конфликтов с классами, используемыми хост-процессом (и другими плагинами). Кодирование Инструкций для Какао имеет некоторое полезное уведомление относительно этой темы.

Одно значение всего процесса, заслуживающее особого внимания, является эффективным идентификатором пользователя (EUID). Существуют обстоятельства, где Ваш плагин авторизации должен переключить EUID на того из пользователя, входящего в систему. При этих обстоятельствах необходимо избегать использования seteuid, потому что это изменяет EUID для всего процесса. Скорее необходимо использовать альтернативу на поток, pthread_setugid_np; Перечисление 5 показывает пример того, как использовать эту подпрограмму.

Перечисление 5  , Создающее файл как определенный пользователь

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/kauth.h>

static int CreateFileAsUserGroup(const char *path, uid_t uid, gid_t gid)
    // Creates a file and returns the file descriptor. On error, returns 
    // -1 and errno is set to an error value.
{
    int     err;
    int     fd;
    int     junk;

    fd = -1;

    err = pthread_setugid_np(uid, gid);
    if (err == 0) {
        fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IRWXU);

        err = errno; // preserve errno across the pthread_setugid_np
        junk = pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE);
        assert(junk == 0);
        errno = err;
    }
    return fd;
}

Смертельные проблемы узла

Системный процесс, размещающий Ваш плагин авторизации, будет обычно оконечный в конце процесса входа в систему, что означает, что Ваш плагин авторизации не будет продолжать выполняться во время сеанса входа в систему. Если Вы хотите сохранить присутствие во время сеанса входа в систему, необходимо использовать некоторый другой механизм (обычно GUI launchd агент или глобальный элемент входа в систему).

Для получения дополнительной информации о launchd агентах и элементах входа в систему, посмотрите Техническое примечание TN2083, 'Демоны и Агенты'

Дополнительные материалы для чтения



История версии документа


ДатаПримечания
16.09.2008

Новый документ, описывающий, как записать код, это координируется с процессом входа в систему.