macOS: アプリ内で管理者権限でコマンドを実行する

macOSのアプリ内で指定したコマンドを管理者権限で実行する。

関数の実装

 //  Authorization系APIの返り値をチェックする
//  @param  status  返り値
//  @param  apiName 実行したAPI名
void checkAuthorizationStatus(OSStatus status, NSString* apiName) {
    if (status != errAuthorizationSuccess) {
        NSString* errorMessage = [NSString stringWithFormat:@"Error %@: %d", apiName, status];
        @throw [NSException exceptionWithName:@"ExecWithAdminPrivilegeException" reason:errorMessage userInfo:nil];
    }
}

//  指定されたコマンドを管理者権限で実行する
//  @param  cmd     実行するコマンド
//  @param  args    コマンドに渡すパラメータ
void execWithAdminPrivilege(const char* cmd, char* args[]) {
    // Create authorization reference
    OSStatus status;
    AuthorizationRef authorizationRef;
    
    // AuthorizationCreate and pass NULL as the initial
    // AuthorizationRights set so that the AuthorizationRef gets created
    // successfully, and then later call AuthorizationCopyRights to
    // determine or extend the allowable rights.
    // http://developer.apple.com/qa/qa2001/qa1172.html
    status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
                                 kAuthorizationFlagDefaults, &authorizationRef);
    checkAuthorizationStatus(status, @"AuthorizationCreate");
    
    // kAuthorizationRightExecute == "system.privilege.admin"
    AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0};
    AuthorizationRights rights = {1, &right};
    AuthorizationFlags flags = kAuthorizationFlagDefaults |
        kAuthorizationFlagInteractionAllowed |
        kAuthorizationFlagPreAuthorize |
        kAuthorizationFlagExtendRights;
    
    
    // Call AuthorizationCopyRights to determine or extend the allowable rights.
    status = AuthorizationCopyRights(authorizationRef, &rights, kAuthorizationEmptyEnvironment, flags, NULL);
    checkAuthorizationStatus(status, @"AuthorizationCopyRights");
    
    status = AuthorizationExecuteWithPrivileges(authorizationRef, cmd,
                                                kAuthorizationFlagDefaults, args, NULL);
    checkAuthorizationStatus(status, @"AuthorizationExecuteWithPrivileges");
    
    // The only way to guarantee that a credential acquired when you
    // request a right is not shared with other authorization instances is
    // to destroy the credential.  To do so, call the AuthorizationFree
    // function with the flag kAuthorizationFlagDestroyRights.
    // http://developer.apple.com/documentation/Security/Conceptual/authorization_concepts/02authconcepts/chapter_2_section_7.html
    status = AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
    checkAuthorizationStatus(status, @"AuthorizationFree");
}

実行例

  • execWithAdminPrivilege()関数のパラメータの注意点。
    • cmdには実行するコマンドをフルパスで指定する。
      */bin/cpなど。
    • argsにはコマンドに指定するパラメータを指定する。配列の最後はNULLにすること。
const char* cmd = "/usr/bin/touch";
char* args[] = {"/Applications/test", NULL};
execWithAdminPrivilege(cmd, args);

実行すると以下のダイアログが表示され、管理者ユーザで認証できれば実行される。 f:id:shindo1687:20191226224045p:plain

※"App Sandbox"が有効になっていると、AuthorizationCopyRights関数でerrAuthorizationDeniedエラーが発生する。