sspi for NTLM or Kerberos
代码
1 /*
2 You can get a locally valid logon session (a network logon session) by using SSPI.
3 Simply specify alternate credentials when you call AcquireCredentialsHandle,
4 and then after you follow the normal InitializeSecurityContext/AcceptSecurityContext
5 handshake, you'll end up with a logon session for the user,
6 without having to have the TCB privilege.
7
8 See the attached example...
9
10 Keith
11 */
12
13 // this version requires Windows 2000
14 #define _WIN32_WINNT 0x500
15 #define UNICODE
16 #include <windows.h>
17 #include <stdio.h>
18 #define SECURITY_WIN32
19 #include <security.h>
20 #pragma comment(lib, "secur32.lib")
21
22 // brain-dead error routine that dumps the last error and exits
23 void _err(const wchar_t* pszFcn, DWORD nErr = GetLastError())
24 {
25
26 wchar_t szErr[256];
27 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, nErr, 0,
28 szErr, sizeof szErr / sizeof *szErr, 0))
29 wprintf(L"%s failed: %s", pszFcn, szErr);
30 else wprintf(L"%s failed: 0x%08X", nErr);
31 exit(1);
32 }
33
34 // Upon success, returns a handle to a NETWORK logon session
35 // for the specified principal (it will *not* have network
36 // credentials). Call CloseHandle to dispose of it when
37 // you are finished.
38 HANDLE _logonUserWithSSPI(wchar_t* pszSSP,
39 DWORD grfDesiredAccessToToken,
40 wchar_t* pszAuthority,
41 wchar_t* pszPrincipal,
42 wchar_t* pszPassword) {
43
44 // the following code loads the SSPI interface DLL
45 // and initializes it, getting a table of function ptrs
46 HINSTANCE hdll = LoadLibrary(L"security.dll");
47 if (!hdll)
48 _err(L"LoadLibrary");
49 INIT_SECURITY_INTERFACE_W initSecurityInterface =
50 (INIT_SECURITY_INTERFACE_W)GetProcAddress(hdll,
51 SECURITY_ENTRYPOINT_ANSIW);
52 if (!initSecurityInterface)
53 _err(L"GetProcAddress");
54 PSecurityFunctionTable pSSPI = initSecurityInterface();
55
56 // here's where we specify the credentials to verify
57 SEC_WINNT_AUTH_IDENTITY_EX authIdent = {
58 SEC_WINNT_AUTH_IDENTITY_VERSION,
59 sizeof authIdent,
60 pszPrincipal, lstrlenW(pszPrincipal),
61 pszAuthority, lstrlenW(pszAuthority),
62 pszPassword, lstrlenW(pszPassword),
63 SEC_WINNT_AUTH_IDENTITY_UNICODE,
64 0, 0
65 };
66
67 // get an SSPI handle for these credentials
68 CredHandle hcredClient;
69 TimeStamp expiryClient;
70 SECURITY_STATUS err =
71 pSSPI->AcquireCredentialsHandle(0, pszSSP,
72 SECPKG_CRED_OUTBOUND,
73 0, &authIdent,
74 0, 0,
75 &hcredClient,
76 &expiryClient);
77 if (err)
78 _err(L"AcquireCredentialsHandle for client", err);
79
80 // use the caller's credentials for the server
81 CredHandle hcredServer;
82 TimeStamp expiryServer;
83 err = pSSPI->AcquireCredentialsHandle(0, pszSSP,
84 SECPKG_CRED_INBOUND,
85 0, 0, 0, 0,
86 &hcredServer,
87 &expiryServer);
88 if (err)
89 _err(L"AcquireCredentialsHandle for server", err);
90
91 CtxtHandle hctxClient;
92 CtxtHandle hctxServer;
93
94 // create two buffers:
95 // one for the client sending tokens to the server,
96 // one for the server sending tokens to the client
97 // (buffer size chosen based on current Kerb SSP setting
98 // for cbMaxToken - you may need to adjust this)
99 BYTE bufC2S[8000];
100 BYTE bufS2C[8000];
101 SecBuffer sbufC2S = { sizeof bufC2S, SECBUFFER_TOKEN, bufC2S };
102 SecBuffer sbufS2C = { sizeof bufS2C, SECBUFFER_TOKEN, bufS2C };
103 SecBufferDesc bdC2S = { SECBUFFER_VERSION, 1, &sbufC2S };
104 SecBufferDesc bdS2C = { SECBUFFER_VERSION, 1, &sbufS2C };
105
106 // don't really need any special context attributes
107 DWORD grfRequiredCtxAttrsClient = ISC_REQ_DATAGRAM;//ISC_REQ_DELEGATE
108 DWORD grfRequiredCtxAttrsServer = ASC_REQ_DATAGRAM;
109
110 // set up some aliases to make it obvious what's happening
111 PCtxtHandle pClientCtxHandleIn = 0;
112 PCtxtHandle pClientCtxHandleOut = &hctxClient;
113 PCtxtHandle pServerCtxHandleIn = 0;
114 PCtxtHandle pServerCtxHandleOut = &hctxServer;
115 SecBufferDesc* pClientInput = 0;
116 SecBufferDesc* pClientOutput = &bdC2S;
117 SecBufferDesc* pServerInput = &bdC2S;
118 SecBufferDesc* pServerOutput = &bdS2C;
119 DWORD grfCtxAttrsClient = 0;
120 DWORD grfCtxAttrsServer = 0;
121 TimeStamp expiryClientCtx;
122 TimeStamp expiryServerCtx;
123
124 // since the caller is acting as the server, we need
125 // a server principal name so that the client will
126 // be able to get a Kerb ticket (if Kerb is used)
127 wchar_t szSPN[256]={0};
128 ULONG cchSPN = sizeof szSPN / sizeof *szSPN;
129 GetUserNameEx(NameSamCompatible, szSPN, &cchSPN);
130
131 //sevice class / host: port / service instance
132
133 // wcscpy (szSPN, TEXT("192.168.1.11"));
134 // wcscpy (szSPN, TEXT("testwork-AD11.testwork.com"));
135 // wcscpy (szSPN, TEXT("192.168.1.11"));
136 // wcscpy (szSPN, TEXT("TESTWORK\\administrator"));
137 // wcscpy (szSPN, TEXT("..."));
138 printf("%ws\n",szSPN);
139 // perform the authentication handshake, playing the
140 // role of both client *and* server.
141 bool bClientContinue = true;
142 bool bServerContinue = true;
143 int count=1;
144 while (bClientContinue || bServerContinue) {
145
146 if (bClientContinue) {
147
148 sbufC2S.cbBuffer = sizeof bufC2S;
149 err = pSSPI->InitializeSecurityContext(
150 &hcredClient, pClientCtxHandleIn,
151 szSPN,
152 grfRequiredCtxAttrsClient,
153 0, SECURITY_NETWORK_DREP,
154 pClientInput, 0,
155 pClientCtxHandleOut,
156 pClientOutput,
157 &grfCtxAttrsClient,
158 &expiryClientCtx);
159 switch (err) {
160 case 0:
161 bClientContinue = false;
162 break;
163 case SEC_I_CONTINUE_NEEDED:
164 pClientCtxHandleIn = pClientCtxHandleOut;
165 pClientInput = pServerOutput;
166 break;
167 default:
168 printf("err=%x\n",err);
169 _err(L"InitializeSecurityContext", err);
170 }
171 }
172
173 if (bServerContinue) {
174 sbufS2C.cbBuffer = sizeof bufS2C;
175 err = pSSPI->AcceptSecurityContext(
176 &hcredServer, pServerCtxHandleIn,
177 pServerInput,
178 grfRequiredCtxAttrsServer,
179 SECURITY_NETWORK_DREP,
180 pServerCtxHandleOut,
181 pServerOutput,
182 &grfCtxAttrsServer,
183 &expiryServerCtx);
184 switch (err) {
185 case 0:
186 bServerContinue = false;
187 break;
188 case SEC_I_CONTINUE_NEEDED:
189 pServerCtxHandleIn = pServerCtxHandleOut;
190 break;
191 default:
192 _err(L"AcceptSecurityContext", err);
193 }
194 }
195 printf("%d bClientContinue %d bServerContinue %d\n",count++,bClientContinue,bServerContinue);
196 }
197 // if everything has gone smoothly, we've now got a logon
198 // session for the client - let's grab the token now
199 pSSPI->ImpersonateSecurityContext(pServerCtxHandleOut);
200 HANDLE htok;
201 if (!OpenThreadToken(GetCurrentThread(),
202 grfDesiredAccessToToken,
203 TRUE, &htok))
204 _err(L"OpenThreadToken");
205 pSSPI->RevertSecurityContext(pServerCtxHandleOut);
206
207 // clean up
208 pSSPI->FreeCredentialsHandle(&hcredClient);
209 pSSPI->FreeCredentialsHandle(&hcredServer);
210 pSSPI->DeleteSecurityContext(pServerCtxHandleOut);
211 pSSPI->DeleteSecurityContext(pClientCtxHandleOut);
212
213 return htok;
214 }
215
216 // here's an example of usage
217 void main() {
218 // use "NTLM" or "Kerberos"
219
220 //Kerbero需要在加入域或者有域环境下才能正确运行
221 HANDLE htok = _logonUserWithSSPI(L"Kerberos",
222 TOKEN_QUERY,
223 NULL,
224 L"administrator",
225 L"!QAZsx");
226 if (htok) {
227 // password is valid!
228 CloseHandle(htok);
229 }
230 }
231
232
2 You can get a locally valid logon session (a network logon session) by using SSPI.
3 Simply specify alternate credentials when you call AcquireCredentialsHandle,
4 and then after you follow the normal InitializeSecurityContext/AcceptSecurityContext
5 handshake, you'll end up with a logon session for the user,
6 without having to have the TCB privilege.
7
8 See the attached example...
9
10 Keith
11 */
12
13 // this version requires Windows 2000
14 #define _WIN32_WINNT 0x500
15 #define UNICODE
16 #include <windows.h>
17 #include <stdio.h>
18 #define SECURITY_WIN32
19 #include <security.h>
20 #pragma comment(lib, "secur32.lib")
21
22 // brain-dead error routine that dumps the last error and exits
23 void _err(const wchar_t* pszFcn, DWORD nErr = GetLastError())
24 {
25
26 wchar_t szErr[256];
27 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, nErr, 0,
28 szErr, sizeof szErr / sizeof *szErr, 0))
29 wprintf(L"%s failed: %s", pszFcn, szErr);
30 else wprintf(L"%s failed: 0x%08X", nErr);
31 exit(1);
32 }
33
34 // Upon success, returns a handle to a NETWORK logon session
35 // for the specified principal (it will *not* have network
36 // credentials). Call CloseHandle to dispose of it when
37 // you are finished.
38 HANDLE _logonUserWithSSPI(wchar_t* pszSSP,
39 DWORD grfDesiredAccessToToken,
40 wchar_t* pszAuthority,
41 wchar_t* pszPrincipal,
42 wchar_t* pszPassword) {
43
44 // the following code loads the SSPI interface DLL
45 // and initializes it, getting a table of function ptrs
46 HINSTANCE hdll = LoadLibrary(L"security.dll");
47 if (!hdll)
48 _err(L"LoadLibrary");
49 INIT_SECURITY_INTERFACE_W initSecurityInterface =
50 (INIT_SECURITY_INTERFACE_W)GetProcAddress(hdll,
51 SECURITY_ENTRYPOINT_ANSIW);
52 if (!initSecurityInterface)
53 _err(L"GetProcAddress");
54 PSecurityFunctionTable pSSPI = initSecurityInterface();
55
56 // here's where we specify the credentials to verify
57 SEC_WINNT_AUTH_IDENTITY_EX authIdent = {
58 SEC_WINNT_AUTH_IDENTITY_VERSION,
59 sizeof authIdent,
60 pszPrincipal, lstrlenW(pszPrincipal),
61 pszAuthority, lstrlenW(pszAuthority),
62 pszPassword, lstrlenW(pszPassword),
63 SEC_WINNT_AUTH_IDENTITY_UNICODE,
64 0, 0
65 };
66
67 // get an SSPI handle for these credentials
68 CredHandle hcredClient;
69 TimeStamp expiryClient;
70 SECURITY_STATUS err =
71 pSSPI->AcquireCredentialsHandle(0, pszSSP,
72 SECPKG_CRED_OUTBOUND,
73 0, &authIdent,
74 0, 0,
75 &hcredClient,
76 &expiryClient);
77 if (err)
78 _err(L"AcquireCredentialsHandle for client", err);
79
80 // use the caller's credentials for the server
81 CredHandle hcredServer;
82 TimeStamp expiryServer;
83 err = pSSPI->AcquireCredentialsHandle(0, pszSSP,
84 SECPKG_CRED_INBOUND,
85 0, 0, 0, 0,
86 &hcredServer,
87 &expiryServer);
88 if (err)
89 _err(L"AcquireCredentialsHandle for server", err);
90
91 CtxtHandle hctxClient;
92 CtxtHandle hctxServer;
93
94 // create two buffers:
95 // one for the client sending tokens to the server,
96 // one for the server sending tokens to the client
97 // (buffer size chosen based on current Kerb SSP setting
98 // for cbMaxToken - you may need to adjust this)
99 BYTE bufC2S[8000];
100 BYTE bufS2C[8000];
101 SecBuffer sbufC2S = { sizeof bufC2S, SECBUFFER_TOKEN, bufC2S };
102 SecBuffer sbufS2C = { sizeof bufS2C, SECBUFFER_TOKEN, bufS2C };
103 SecBufferDesc bdC2S = { SECBUFFER_VERSION, 1, &sbufC2S };
104 SecBufferDesc bdS2C = { SECBUFFER_VERSION, 1, &sbufS2C };
105
106 // don't really need any special context attributes
107 DWORD grfRequiredCtxAttrsClient = ISC_REQ_DATAGRAM;//ISC_REQ_DELEGATE
108 DWORD grfRequiredCtxAttrsServer = ASC_REQ_DATAGRAM;
109
110 // set up some aliases to make it obvious what's happening
111 PCtxtHandle pClientCtxHandleIn = 0;
112 PCtxtHandle pClientCtxHandleOut = &hctxClient;
113 PCtxtHandle pServerCtxHandleIn = 0;
114 PCtxtHandle pServerCtxHandleOut = &hctxServer;
115 SecBufferDesc* pClientInput = 0;
116 SecBufferDesc* pClientOutput = &bdC2S;
117 SecBufferDesc* pServerInput = &bdC2S;
118 SecBufferDesc* pServerOutput = &bdS2C;
119 DWORD grfCtxAttrsClient = 0;
120 DWORD grfCtxAttrsServer = 0;
121 TimeStamp expiryClientCtx;
122 TimeStamp expiryServerCtx;
123
124 // since the caller is acting as the server, we need
125 // a server principal name so that the client will
126 // be able to get a Kerb ticket (if Kerb is used)
127 wchar_t szSPN[256]={0};
128 ULONG cchSPN = sizeof szSPN / sizeof *szSPN;
129 GetUserNameEx(NameSamCompatible, szSPN, &cchSPN);
130
131 //sevice class / host: port / service instance
132
133 // wcscpy (szSPN, TEXT("192.168.1.11"));
134 // wcscpy (szSPN, TEXT("testwork-AD11.testwork.com"));
135 // wcscpy (szSPN, TEXT("192.168.1.11"));
136 // wcscpy (szSPN, TEXT("TESTWORK\\administrator"));
137 // wcscpy (szSPN, TEXT("..."));
138 printf("%ws\n",szSPN);
139 // perform the authentication handshake, playing the
140 // role of both client *and* server.
141 bool bClientContinue = true;
142 bool bServerContinue = true;
143 int count=1;
144 while (bClientContinue || bServerContinue) {
145
146 if (bClientContinue) {
147
148 sbufC2S.cbBuffer = sizeof bufC2S;
149 err = pSSPI->InitializeSecurityContext(
150 &hcredClient, pClientCtxHandleIn,
151 szSPN,
152 grfRequiredCtxAttrsClient,
153 0, SECURITY_NETWORK_DREP,
154 pClientInput, 0,
155 pClientCtxHandleOut,
156 pClientOutput,
157 &grfCtxAttrsClient,
158 &expiryClientCtx);
159 switch (err) {
160 case 0:
161 bClientContinue = false;
162 break;
163 case SEC_I_CONTINUE_NEEDED:
164 pClientCtxHandleIn = pClientCtxHandleOut;
165 pClientInput = pServerOutput;
166 break;
167 default:
168 printf("err=%x\n",err);
169 _err(L"InitializeSecurityContext", err);
170 }
171 }
172
173 if (bServerContinue) {
174 sbufS2C.cbBuffer = sizeof bufS2C;
175 err = pSSPI->AcceptSecurityContext(
176 &hcredServer, pServerCtxHandleIn,
177 pServerInput,
178 grfRequiredCtxAttrsServer,
179 SECURITY_NETWORK_DREP,
180 pServerCtxHandleOut,
181 pServerOutput,
182 &grfCtxAttrsServer,
183 &expiryServerCtx);
184 switch (err) {
185 case 0:
186 bServerContinue = false;
187 break;
188 case SEC_I_CONTINUE_NEEDED:
189 pServerCtxHandleIn = pServerCtxHandleOut;
190 break;
191 default:
192 _err(L"AcceptSecurityContext", err);
193 }
194 }
195 printf("%d bClientContinue %d bServerContinue %d\n",count++,bClientContinue,bServerContinue);
196 }
197 // if everything has gone smoothly, we've now got a logon
198 // session for the client - let's grab the token now
199 pSSPI->ImpersonateSecurityContext(pServerCtxHandleOut);
200 HANDLE htok;
201 if (!OpenThreadToken(GetCurrentThread(),
202 grfDesiredAccessToToken,
203 TRUE, &htok))
204 _err(L"OpenThreadToken");
205 pSSPI->RevertSecurityContext(pServerCtxHandleOut);
206
207 // clean up
208 pSSPI->FreeCredentialsHandle(&hcredClient);
209 pSSPI->FreeCredentialsHandle(&hcredServer);
210 pSSPI->DeleteSecurityContext(pServerCtxHandleOut);
211 pSSPI->DeleteSecurityContext(pClientCtxHandleOut);
212
213 return htok;
214 }
215
216 // here's an example of usage
217 void main() {
218 // use "NTLM" or "Kerberos"
219
220 //Kerbero需要在加入域或者有域环境下才能正确运行
221 HANDLE htok = _logonUserWithSSPI(L"Kerberos",
222 TOKEN_QUERY,
223 NULL,
224 L"administrator",
225 L"!QAZsx");
226 if (htok) {
227 // password is valid!
228 CloseHandle(htok);
229 }
230 }
231
232