Win32 API 日本語リファレンス
ホーム › Networking.ActiveDirectory › DsWriteAccountSpnA

DsWriteAccountSpnA

関数
アカウントオブジェクトにSPNを書き込む(ANSI版)。
DLLNTDSAPI.dll文字セットANSI (-A)呼出規約winapi対応OSWindows Vista 以降

シグネチャ

// NTDSAPI.dll  (ANSI / -A)
#include <windows.h>

DWORD DsWriteAccountSpnA(
    HANDLE hDS,
    DS_SPN_WRITE_OP Operation,
    LPCSTR pszAccount,
    DWORD cSpn,
    LPCSTR* rpszSpn
);

パラメーター

名前型方向説明
hDSHANDLEinDSBind 関数または DSBindWithCred 関数から取得したディレクトリサービスハンドルを格納します。
OperationDS_SPN_WRITE_OPinDsWriteAccountSpn が実行する操作を指定する DS_SPN_WRITE_OP の値のいずれかを格納します。
pszAccountLPCSTRinActive Directory Domain Services 内のユーザーオブジェクトまたはコンピューターオブジェクトの識別名 (DN) を指定する、定数の null 終端文字列へのポインターです。呼び出し元は、このオブジェクトの servicePrincipalName プロパティに対する書き込みアクセス権を持っている必要があります。
cSpnDWORDinrpszSpn に含まれる SPN の数を指定します。この値が 0 で、かつ Operation に DS_SPN_REPLACE_SPN_OP が指定されている場合、この関数は指定されたアカウントの servicePrincipalName 属性からすべての値を削除します。
rpszSpnLPCSTR*inpszAccount パラメーターで識別されるアカウントに追加する、またはそのアカウントから削除する SPN を指定する、定数の null 終端文字列の配列へのポインターです。サービスの SPN を構成するには DsGetSpn 関数を使用します。

戻り値の型: DWORD

公式ドキュメント

Active Directory Domain Services 内の指定されたユーザーまたはコンピューターのアカウントオブジェクトの servicePrincipalName 属性に、サービスプリンシパル名 (SPN) の配列を書き込みます。(ANSI)

戻り値

成功した場合は ERROR_SUCCESS を返します。失敗した場合は Win32、RPC、またはディレクトリサービスのエラーを返します。

解説(Remarks)

DsWriteAccountSpn 関数は、1 つ以上のサービスインスタンスに対する SPN を登録します。SPN は、信頼された認証サービスと組み合わせて、クライアントがサービスを認証するために使用します。アプリケーションまたはサービスが他のサービスを識別する SPN を不正に登録するようなセキュリティ攻撃を防ぐため、ユーザーアカウントおよびコンピューターアカウントの既定の DACL では、ほとんどの場合ドメイン管理者のみが SPN を登録できるようになっています。

この規則の例外の 1 つとして、LocalSystem アカウントで実行されているサービスは、SPN で指定されたホストがそのサービスを実行しているコンピューターの DNS 名または NetBIOS 名である場合に限り、DsWriteAccountSpn を呼び出して "ServiceClass/Host:Port" という形式の単純な SPN を登録できます。

もう 1 つの例外として、コンピューターアカウントの既定の DACL では、一定の制約のもとで、呼び出し元が自分自身に対して SPN を登録することが許可されています。たとえば、コンピューターアカウントは "host/<computername>" という形式の、自身のコンピューター名に関連する SPN を持つことができます。コンピューター名が SPN に含まれているため、その SPN は許可されます。

任意の SPN の書き込みを許可するように DSA が構成されている場合、上記の規則はいずれも適用されません。ただし、これはセキュリティを低下させるため推奨されません。

DsWriteAccountSpn に渡された SPN は、実際には pszAccount で指定されたコンピューターオブジェクトの Service-Principal-Name 属性に追加されます。この呼び出しは、アカウントオブジェクトが格納されているドメインコントローラーに対して RPC を使用して行われ、そのアカウントで許可される SPN に関するポリシーを安全に適用できるようになっています。LDAP を使用して SPN プロパティに直接書き込むことは許可されておらず、すべての書き込みはこの RPC 呼び出しを経由する必要があります。LDAP による読み取りは許可されています。

SPN の設定に必要なアクセス許可

アカウントに任意の SPN を書き込むには、書き込みを行う側に "Write ServicePrincipalName" 権限が必要ですが、この権限はアカウントを作成した人には既定では付与されません。アカウントを作成した人には "Write validated SPN" 権限 (マシンアカウントにのみ存在します) が付与されます。

以下は、マシンアカウントに対するユーザーごとの権限の概要です。

ユーザーの種類 権限
アカウントを作成した人 Write validated SPN
Account Operators Write SPN および Write Validated SPN
Authenticated Users なし
(自分自身) Write Validated SPN

ユーザーアカウントには "Validated SPN" プロパティや "Write SPN" 権限は存在しません。代わりに、"Write public information" プロパティセットによって、任意の SPN を作成する権限が付与されます。

メモ

ntdsapi.h ヘッダーは、UNICODE プリプロセッサ定数の定義に基づいてこの関数の ANSI 版または Unicode 版を自動的に選択するエイリアスとして DsWriteAccountSpn を定義しています。エンコード中立のエイリアスと、エンコード中立でないコードを混在させて使用すると、不一致が生じてコンパイルエラーや実行時エラーの原因となることがあります。詳細については、「Conventions for Function Prototypes」を参照してください。

出典・ライセンス: 上記「公式ドキュメント」の内容は Microsoft の Win32 API ドキュメント(MicrosoftDocs/sdk-api)を日本語に翻訳・改変したものです。© Microsoft Corporation. CC BY 4.0 で提供。
Microsoft 公式リファレンス: 英語 (en-us) · 日本語 (ja-jp) · 原文ソース (GitHub)

各言語での呼び出し定義

// NTDSAPI.dll  (ANSI / -A)
#include <windows.h>

DWORD DsWriteAccountSpnA(
    HANDLE hDS,
    DS_SPN_WRITE_OP Operation,
    LPCSTR pszAccount,
    DWORD cSpn,
    LPCSTR* rpszSpn
);
[DllImport("NTDSAPI.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
static extern uint DsWriteAccountSpnA(
    IntPtr hDS,   // HANDLE
    int Operation,   // DS_SPN_WRITE_OP
    [MarshalAs(UnmanagedType.LPStr)] string pszAccount,   // LPCSTR
    uint cSpn,   // DWORD
    IntPtr rpszSpn   // LPCSTR*
);
<DllImport("NTDSAPI.dll", CharSet:=CharSet.Ansi, ExactSpelling:=True)>
Public Shared Function DsWriteAccountSpnA(
    hDS As IntPtr,   ' HANDLE
    Operation As Integer,   ' DS_SPN_WRITE_OP
    <MarshalAs(UnmanagedType.LPStr)> pszAccount As String,   ' LPCSTR
    cSpn As UInteger,   ' DWORD
    rpszSpn As IntPtr   ' LPCSTR*
) As UInteger
End Function
' hDS : HANDLE
' Operation : DS_SPN_WRITE_OP
' pszAccount : LPCSTR
' cSpn : DWORD
' rpszSpn : LPCSTR*
Declare PtrSafe Function DsWriteAccountSpnA Lib "ntdsapi" ( _
    ByVal hDS As LongPtr, _
    ByVal Operation As Long, _
    ByVal pszAccount As String, _
    ByVal cSpn As Long, _
    ByVal rpszSpn As LongPtr) As Long
' VBA7前提(PtrSafe)。32bit Office では LongPtr→Long。Integer=16bit / Long=32bit / LongLong=64bit。
import ctypes
from ctypes import wintypes

DsWriteAccountSpnA = ctypes.windll.ntdsapi.DsWriteAccountSpnA
DsWriteAccountSpnA.restype = wintypes.DWORD
DsWriteAccountSpnA.argtypes = [
    wintypes.HANDLE,  # hDS : HANDLE
    ctypes.c_int,  # Operation : DS_SPN_WRITE_OP
    wintypes.LPCSTR,  # pszAccount : LPCSTR
    wintypes.DWORD,  # cSpn : DWORD
    ctypes.c_void_p,  # rpszSpn : LPCSTR*
]
require 'fiddle'
require 'fiddle/import'

lib = Fiddle.dlopen('NTDSAPI.dll')
DsWriteAccountSpnA = Fiddle::Function.new(
  lib['DsWriteAccountSpnA'],
  [
    Fiddle::TYPE_VOIDP,  # hDS : HANDLE
    Fiddle::TYPE_INT,  # Operation : DS_SPN_WRITE_OP
    Fiddle::TYPE_VOIDP,  # pszAccount : LPCSTR
    -Fiddle::TYPE_INT,  # cSpn : DWORD
    Fiddle::TYPE_VOIDP,  # rpszSpn : LPCSTR*
  ],
  -Fiddle::TYPE_INT)
#[link(name = "ntdsapi")]
extern "system" {
    fn DsWriteAccountSpnA(
        hDS: *mut core::ffi::c_void,  // HANDLE
        Operation: i32,  // DS_SPN_WRITE_OP
        pszAccount: *const u8,  // LPCSTR
        cSpn: u32,  // DWORD
        rpszSpn: *const *const u8  // LPCSTR*
    ) -> u32;
}
// crates: windows-sys provides ready-made bindings for this API.
$sig = @"
[DllImport("NTDSAPI.dll", CharSet = CharSet.Ansi)]
public static extern uint DsWriteAccountSpnA(IntPtr hDS, int Operation, [MarshalAs(UnmanagedType.LPStr)] string pszAccount, uint cSpn, IntPtr rpszSpn);
"@
$api = Add-Type -MemberDefinition $sig -Name 'NTDSAPI_DsWriteAccountSpnA' -Namespace Win32 -PassThru
# $api::DsWriteAccountSpnA(hDS, Operation, pszAccount, cSpn, rpszSpn)
#uselib "NTDSAPI.dll"
#func global DsWriteAccountSpnA "DsWriteAccountSpnA" sptr, sptr, sptr, sptr, sptr
; DsWriteAccountSpnA hDS, Operation, pszAccount, cSpn, varptr(rpszSpn)   ; 戻り値は stat
; hDS : HANDLE -> "sptr"
; Operation : DS_SPN_WRITE_OP -> "sptr"
; pszAccount : LPCSTR -> "sptr"
; cSpn : DWORD -> "sptr"
; rpszSpn : LPCSTR* -> "sptr"
; ※HSP3.7は #func のため戻り値はシステム変数 stat に格納されます。
出力引数:
#uselib "NTDSAPI.dll"
#cfunc global DsWriteAccountSpnA "DsWriteAccountSpnA" sptr, int, str, int, var
; res = DsWriteAccountSpnA(hDS, Operation, pszAccount, cSpn, rpszSpn)
; hDS : HANDLE -> "sptr"
; Operation : DS_SPN_WRITE_OP -> "int"
; pszAccount : LPCSTR -> "str"
; cSpn : DWORD -> "int"
; rpszSpn : LPCSTR* -> "var"
; ※出力/バッファ引数は var 方式(変数を直接渡す)。varptr 方式にも切替可。
出力引数:
; DWORD DsWriteAccountSpnA(HANDLE hDS, DS_SPN_WRITE_OP Operation, LPCSTR pszAccount, DWORD cSpn, LPCSTR* rpszSpn)
#uselib "NTDSAPI.dll"
#cfunc global DsWriteAccountSpnA "DsWriteAccountSpnA" intptr, int, str, int, var
; res = DsWriteAccountSpnA(hDS, Operation, pszAccount, cSpn, rpszSpn)
; hDS : HANDLE -> "intptr"
; Operation : DS_SPN_WRITE_OP -> "int"
; pszAccount : LPCSTR -> "str"
; cSpn : DWORD -> "int"
; rpszSpn : LPCSTR* -> "var"
; ※出力/バッファ引数は var 方式(変数を直接渡す)。varptr 方式にも切替可。
import (
	"golang.org/x/sys/windows"
	"unsafe"
)

var (
	ntdsapi = windows.NewLazySystemDLL("NTDSAPI.dll")
	procDsWriteAccountSpnA = ntdsapi.NewProc("DsWriteAccountSpnA")
)

// hDS (HANDLE), Operation (DS_SPN_WRITE_OP), pszAccount (LPCSTR), cSpn (DWORD), rpszSpn (LPCSTR*)
r1, _, err := procDsWriteAccountSpnA.Call(
	uintptr(hDS),
	uintptr(Operation),
	uintptr(unsafe.Pointer(windows.BytePtrFromString(pszAccount))),
	uintptr(cSpn),
	uintptr(rpszSpn),
)
_ = err  // syscall.Errno (valid when the call sets last-error)
_ = r1   // DWORD
function DsWriteAccountSpnA(
  hDS: THandle;   // HANDLE
  Operation: Integer;   // DS_SPN_WRITE_OP
  pszAccount: PAnsiChar;   // LPCSTR
  cSpn: DWORD;   // DWORD
  rpszSpn: PPAnsiChar   // LPCSTR*
): DWORD; stdcall;
  external 'NTDSAPI.dll' name 'DsWriteAccountSpnA';
result := DllCall("NTDSAPI\DsWriteAccountSpnA"
    , "Ptr", hDS   ; HANDLE
    , "Int", Operation   ; DS_SPN_WRITE_OP
    , "AStr", pszAccount   ; LPCSTR
    , "UInt", cSpn   ; DWORD
    , "Ptr", rpszSpn   ; LPCSTR*
    , "UInt")   ; return: DWORD
●DsWriteAccountSpnA(hDS, Operation, pszAccount, cSpn, rpszSpn) = DLL("NTDSAPI.dll", "dword DsWriteAccountSpnA(void*, int, char*, dword, void*)")
# 呼び出し: DsWriteAccountSpnA(hDS, Operation, pszAccount, cSpn, rpszSpn)
# hDS : HANDLE -> "void*"
# Operation : DS_SPN_WRITE_OP -> "int"
# pszAccount : LPCSTR -> "char*"
# cSpn : DWORD -> "dword"
# rpszSpn : LPCSTR* -> "void*"
# なでしこ1は32bit・ANSI(Shift_JIS)。文字列=char*(ANSI)、ポインタ/ハンドル=void*(4byte)。
const std = @import("std");

extern "ntdsapi" fn DsWriteAccountSpnA(
    hDS: ?*anyopaque, // HANDLE
    Operation: i32, // DS_SPN_WRITE_OP
    pszAccount: [*c]const u8, // LPCSTR
    cSpn: u32, // DWORD
    rpszSpn: [*c][*c]u8 // LPCSTR*
) callconv(std.os.windows.WINAPI) u32;
proc DsWriteAccountSpnA(
    hDS: pointer,  # HANDLE
    Operation: int32,  # DS_SPN_WRITE_OP
    pszAccount: cstring,  # LPCSTR
    cSpn: uint32,  # DWORD
    rpszSpn: ptr cstring  # LPCSTR*
): uint32 {.importc: "DsWriteAccountSpnA", stdcall, dynlib: "NTDSAPI.dll".}
pragma(lib, "ntdsapi");
extern(Windows)
uint DsWriteAccountSpnA(
    void* hDS,   // HANDLE
    int Operation,   // DS_SPN_WRITE_OP
    const(char)* pszAccount,   // LPCSTR
    uint cSpn,   // DWORD
    char** rpszSpn   // LPCSTR*
);
ccall((:DsWriteAccountSpnA, "NTDSAPI.dll"), stdcall, UInt32,
      (Ptr{Cvoid}, Int32, Cstring, UInt32, Ptr{Ptr{UInt8}}),
      hDS, Operation, pszAccount, cSpn, rpszSpn)
# hDS : HANDLE -> Ptr{Cvoid}
# Operation : DS_SPN_WRITE_OP -> Int32
# pszAccount : LPCSTR -> Cstring
# cSpn : DWORD -> UInt32
# rpszSpn : LPCSTR* -> Ptr{Ptr{UInt8}}
# stdcall は 32bit のみ意味を持つ(x64 では無視)。
local ffi = require("ffi")
ffi.cdef[[
uint32_t DsWriteAccountSpnA(
    void* hDS,
    int32_t Operation,
    const char* pszAccount,
    uint32_t cSpn,
    char** rpszSpn);
]]
local ntdsapi = ffi.load("ntdsapi")
-- ntdsapi.DsWriteAccountSpnA(hDS, Operation, pszAccount, cSpn, rpszSpn)
-- hDS : HANDLE
-- Operation : DS_SPN_WRITE_OP
-- pszAccount : LPCSTR
-- cSpn : DWORD
-- rpszSpn : LPCSTR*
-- 構造体/GUIDへのポインタは cdef が通るよう void* で表記(実型は各引数コメント参照)。値渡し構造体・enum は対応する typedef を cdef に追加すること。
const koffi = require('koffi');
const lib = koffi.load('NTDSAPI.dll');
const DsWriteAccountSpnA = lib.func('__stdcall', 'DsWriteAccountSpnA', 'uint32_t', ['void *', 'int32_t', 'str', 'uint32_t', 'void *']);
// DsWriteAccountSpnA(hDS, Operation, pszAccount, cSpn, rpszSpn)
// hDS : HANDLE -> 'void *'
// Operation : DS_SPN_WRITE_OP -> 'int32_t'
// pszAccount : LPCSTR -> 'str'
// cSpn : DWORD -> 'uint32_t'
// rpszSpn : LPCSTR* -> 'void *'
// 出力ポインタは koffi.out(...) で包む。構造体は koffi.struct で定義。
const lib = Deno.dlopen("NTDSAPI.dll", {
  DsWriteAccountSpnA: { parameters: ["pointer", "i32", "buffer", "u32", "pointer"], result: "u32" },
});
// lib.symbols.DsWriteAccountSpnA(hDS, Operation, pszAccount, cSpn, rpszSpn)
// hDS : HANDLE -> "pointer"
// Operation : DS_SPN_WRITE_OP -> "i32"
// pszAccount : LPCSTR -> "buffer"
// cSpn : DWORD -> "u32"
// rpszSpn : LPCSTR* -> "pointer"
// 文字列は "buffer"。ANSI(-A) は new TextEncoder() で UTF-8/ANSI バイト列(末尾に \x00)を渡す。
// 値渡し構造体は { struct: [ ...field types... ] } を使用。
<?php
$ffi = FFI::cdef(<<<C
uint32_t DsWriteAccountSpnA(
    void* hDS,
    int32_t Operation,
    const char* pszAccount,
    uint32_t cSpn,
    char** rpszSpn);
C, "NTDSAPI.dll");
// $ffi->DsWriteAccountSpnA(hDS, Operation, pszAccount, cSpn, rpszSpn);
// hDS : HANDLE
// Operation : DS_SPN_WRITE_OP
// pszAccount : LPCSTR
// cSpn : DWORD
// rpszSpn : LPCSTR*
// 構造体/GUIDへのポインタは cdef が通るよう void* で表記(実型は各引数コメント参照)。値渡し構造体・enum は対応する typedef を cdef に追加すること。
// WINAPI(stdcall): x64 では呼出規約が統一されるため問題なし。x86 では __stdcall 対応のラッパが必要な場合あり。
import com.sun.jna.*;
import com.sun.jna.ptr.*;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

public interface Ntdsapi extends StdCallLibrary {
    Ntdsapi INSTANCE = Native.load("ntdsapi", Ntdsapi.class, W32APIOptions.ASCII_OPTIONS);
    int DsWriteAccountSpnA(
        Pointer hDS,   // HANDLE
        int Operation,   // DS_SPN_WRITE_OP
        String pszAccount,   // LPCSTR
        int cSpn,   // DWORD
        PointerByReference rpszSpn   // LPCSTR*
    );
}
@[Link("ntdsapi")]
lib LibNTDSAPI
  fun DsWriteAccountSpnA = DsWriteAccountSpnA(
    hDS : Void*,   # HANDLE
    Operation : Int32,   # DS_SPN_WRITE_OP
    pszAccount : UInt8*,   # LPCSTR
    cSpn : UInt32,   # DWORD
    rpszSpn : UInt8**   # LPCSTR*
  ) : UInt32
end
# 構造体/GUID/enum は lib 内に対応する型定義が必要。
# 呼出規約: x64 は規約統一のため OK。x86(32bit)は WINAPI=stdcall だが Crystal の fun に stdcall 付与構文がなく非対応。
import 'dart:ffi';
import 'package:ffi/ffi.dart';

typedef DsWriteAccountSpnANative = Uint32 Function(Pointer<Void>, Int32, Pointer<Utf8>, Uint32, Pointer<Pointer<Utf8>>);
typedef DsWriteAccountSpnADart = int Function(Pointer<Void>, int, Pointer<Utf8>, int, Pointer<Pointer<Utf8>>);
final DsWriteAccountSpnA = DynamicLibrary.open('NTDSAPI.dll')
    .lookupFunction<DsWriteAccountSpnANative, DsWriteAccountSpnADart>('DsWriteAccountSpnA');
// hDS : HANDLE -> Pointer<Void>
// Operation : DS_SPN_WRITE_OP -> Int32
// pszAccount : LPCSTR -> Pointer<Utf8>
// cSpn : DWORD -> Uint32
// rpszSpn : LPCSTR* -> Pointer<Pointer<Utf8>>
// 文字列は package:ffi の "...".toNativeUtf16()/toNativeUtf8() で変換。
{$mode objfpc}{$H+}
function DsWriteAccountSpnA(
  hDS: THandle;   // HANDLE
  Operation: Integer;   // DS_SPN_WRITE_OP
  pszAccount: PAnsiChar;   // LPCSTR
  cSpn: DWORD;   // DWORD
  rpszSpn: PPAnsiChar   // LPCSTR*
): DWORD; stdcall;
  external 'NTDSAPI.dll' name 'DsWriteAccountSpnA';
import Foreign
import Foreign.C.Types
import Foreign.C.String

foreign import stdcall safe "DsWriteAccountSpnA"
  c_DsWriteAccountSpnA :: Ptr () -> Int32 -> CString -> Word32 -> Ptr CString -> IO Word32
-- hDS : HANDLE -> Ptr ()
-- Operation : DS_SPN_WRITE_OP -> Int32
-- pszAccount : LPCSTR -> CString
-- cSpn : DWORD -> Word32
-- rpszSpn : LPCSTR* -> Ptr CString
-- 要 GHC(Windows)。stdcall は x64 では ccall として扱われる。ブロックする API は safe 呼び出し推奨。
open Ctypes
open Foreign

let dswriteaccountspna =
  foreign "DsWriteAccountSpnA"
    ((ptr void) @-> int32_t @-> string @-> uint32_t @-> (ptr string) @-> returning uint32_t)
(* hDS : HANDLE -> (ptr void) *)
(* Operation : DS_SPN_WRITE_OP -> int32_t *)
(* pszAccount : LPCSTR -> string *)
(* cSpn : DWORD -> uint32_t *)
(* rpszSpn : LPCSTR* -> (ptr string) *)
(* foreign は cdecl 前提。x64 Windows では WINAPI と一致。構造体は ctypes structure を定義のこと。 *)
(cffi:define-foreign-library ntdsapi (t "NTDSAPI.dll"))
(cffi:use-foreign-library ntdsapi)

(cffi:defcfun ("DsWriteAccountSpnA" ds-write-account-spn-a :convention :stdcall) :uint32
  (h-ds :pointer)   ; HANDLE
  (operation :int32)   ; DS_SPN_WRITE_OP
  (psz-account :string)   ; LPCSTR
  (c-spn :uint32)   ; DWORD
  (rpsz-spn :pointer))   ; LPCSTR*
; isize/usize(INT_PTR/SIZE_T)は x64 前提で :int64/:uint64。x86 では :int32/:uint32。
use Win32::API;
my $DsWriteAccountSpnA = Win32::API::More->new('NTDSAPI',
    'DWORD DsWriteAccountSpnA(HANDLE hDS, int Operation, LPCSTR pszAccount, DWORD cSpn, LPVOID rpszSpn)');
# my $ret = $DsWriteAccountSpnA->Call($hDS, $Operation, $pszAccount, $cSpn, $rpszSpn);
# hDS : HANDLE -> HANDLE
# Operation : DS_SPN_WRITE_OP -> int
# pszAccount : LPCSTR -> LPCSTR
# cSpn : DWORD -> DWORD
# rpszSpn : LPCSTR* -> LPVOID
# 値渡し構造体は pack() した文字列、または Win32::API::Struct を使用。

関連項目

文字セット違い
公式の関連項目
使用する型