Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

467 lines
11 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // localfile.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines the class LocalFile.
  12. //
  13. ///////////////////////////////////////////////////////////////////////////////
  14. #include <ias.h>
  15. #include <iasutf8.h>
  16. #include <sdoias.h>
  17. #include <algorithm>
  18. #include <localfile.h>
  19. #include <classattr.h>
  20. #include <formbuf.h>
  21. #define STACK_ALLOC(type, num) (type*)_alloca(sizeof(type) * (num))
  22. //////////
  23. // Misc. enumerator constants.
  24. //////////
  25. const LONG INET_LOG_FORMAT_INTERNET_STD = 0;
  26. const LONG INET_LOG_FORMAT_NCSA = 3;
  27. const LONG INET_LOG_FORMAT_ODBC_RADIUS = 0xFFFF;
  28. //////////
  29. // Inserts a class attribute into the request.
  30. //////////
  31. extern "C"
  32. HRESULT
  33. WINAPI
  34. InsertClassAttribute(
  35. IAttributesRaw* pRaw
  36. )
  37. {
  38. //////////
  39. // Check if this was proxied.
  40. //////////
  41. PIASATTRIBUTE attr = IASPeekAttribute(
  42. pRaw,
  43. IAS_ATTRIBUTE_PROVIDER_TYPE,
  44. IASTYPE_ENUM
  45. );
  46. if (attr && attr->Value.Enumerator == IAS_PROVIDER_RADIUS_PROXY)
  47. {
  48. return S_OK;
  49. }
  50. //////////
  51. // Check if Generate Class Attribute is disabled
  52. //////////
  53. PIASATTRIBUTE generateClassAttr = IASPeekAttribute(
  54. pRaw,
  55. IAS_ATTRIBUTE_GENERATE_CLASS_ATTRIBUTE,
  56. IASTYPE_BOOLEAN
  57. );
  58. if (generateClassAttr && generateClassAttr->Value.Boolean == FALSE)
  59. {
  60. return S_OK;
  61. }
  62. //////////
  63. // Create a new class attribute
  64. // Do not remove any existing class attribute.
  65. //////////
  66. ATTRIBUTEPOSITION pos;
  67. pos.pAttribute = IASClass::createAttribute(NULL);
  68. if (pos.pAttribute == NULL) { return E_OUTOFMEMORY; }
  69. //////////
  70. // Insert into the request.
  71. //////////
  72. HRESULT hr = pRaw->AddAttributes(1, &pos);
  73. IASAttributeRelease(pos.pAttribute);
  74. return hr;
  75. }
  76. LocalFile::LocalFile() throw ()
  77. : computerNameLen(0)
  78. {
  79. }
  80. STDMETHODIMP LocalFile::Initialize()
  81. {
  82. // Get the Unicode computer name.
  83. WCHAR uniName[MAX_COMPUTERNAME_LENGTH + 1];
  84. DWORD len = sizeof(uniName) / sizeof(WCHAR);
  85. if (!GetComputerNameW(uniName, &len))
  86. {
  87. // If it failed, we'll just use an empty string.
  88. len = 0;
  89. }
  90. // Convert the Unicode to UTF-8.
  91. computerNameLen = IASUnicodeToUtf8(uniName, len, computerName);
  92. IASClass::initialize();
  93. return Accountant::Initialize();
  94. }
  95. STDMETHODIMP LocalFile::Shutdown()
  96. {
  97. log.Close();
  98. return Accountant::Shutdown();
  99. }
  100. STDMETHODIMP LocalFile::PutProperty(LONG Id, VARIANT *pValue)
  101. {
  102. if (pValue == NULL) { return E_INVALIDARG; }
  103. switch (Id)
  104. {
  105. case PROPERTY_ACCOUNTING_LOG_OPEN_NEW_FREQUENCY:
  106. {
  107. if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; }
  108. switch (V_I4(pValue))
  109. {
  110. case IAS_LOGGING_UNLIMITED_SIZE:
  111. case IAS_LOGGING_WHEN_FILE_SIZE_REACHES:
  112. case IAS_LOGGING_DAILY:
  113. case IAS_LOGGING_WEEKLY:
  114. case IAS_LOGGING_MONTHLY:
  115. return log.SetPeriod(
  116. static_cast<NEW_LOG_FILE_FREQUENCY>(
  117. V_I4(pValue)
  118. )
  119. );
  120. break;
  121. default:
  122. return E_INVALIDARG;
  123. }
  124. break;
  125. }
  126. case PROPERTY_ACCOUNTING_LOG_OPEN_NEW_SIZE:
  127. {
  128. if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; }
  129. if (V_I4(pValue) <= 0) { return E_INVALIDARG; }
  130. log.SetMaxSize(V_I4(pValue) * 0x100000ui64);
  131. break;
  132. }
  133. case PROPERTY_ACCOUNTING_LOG_FILE_DIRECTORY:
  134. {
  135. if (V_VT(pValue) != VT_BSTR) { return DISP_E_TYPEMISMATCH; }
  136. if (V_BSTR(pValue) == NULL)
  137. { return E_INVALIDARG; }
  138. return log.SetDirectory(V_BSTR(pValue));
  139. break;
  140. }
  141. case PROPERTY_ACCOUNTING_LOG_IAS1_FORMAT:
  142. {
  143. if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; }
  144. switch (V_I4(pValue))
  145. {
  146. case INET_LOG_FORMAT_ODBC_RADIUS:
  147. format = formatODBCRecord;
  148. break;
  149. case INET_LOG_FORMAT_INTERNET_STD:
  150. format = formatW3CRecord;
  151. break;
  152. default:
  153. return E_INVALIDARG;
  154. }
  155. break;
  156. }
  157. case PROPERTY_ACCOUNTING_LOG_DELETE_IF_FULL:
  158. {
  159. if (V_VT(pValue) != VT_BOOL) { return DISP_E_TYPEMISMATCH; }
  160. log.SetDeleteIfFull((V_BOOL(pValue)) != 0);
  161. break;
  162. }
  163. default:
  164. {
  165. return Accountant::PutProperty(Id, pValue);
  166. }
  167. }
  168. return S_OK;
  169. }
  170. void LocalFile::Process(IASTL::IASRequest& request)
  171. {
  172. // Do some custom preprocessing.
  173. switch (request.get_Request())
  174. {
  175. case IAS_REQUEST_ACCOUNTING:
  176. {
  177. if (request.get_Response() == IAS_RESPONSE_INVALID)
  178. {
  179. request.SetResponse(IAS_RESPONSE_ACCOUNTING, S_OK);
  180. }
  181. break;
  182. }
  183. case IAS_REQUEST_ACCESS_REQUEST:
  184. {
  185. InsertClassAttribute(request);
  186. break;
  187. }
  188. default:
  189. {
  190. break;
  191. }
  192. }
  193. // Create a FormattedBuffer of the correct type.
  194. FormattedBuffer buffer((format == formatODBCRecord) ? '\"' : '\0');
  195. RecordEvent(&buffer, request);
  196. }
  197. void LocalFile::InsertRecord(
  198. void* context,
  199. IASTL::IASRequest& request,
  200. const SYSTEMTIME& localTime,
  201. PATTRIBUTEPOSITION first,
  202. PATTRIBUTEPOSITION last
  203. )
  204. {
  205. FormattedBuffer& buffer = *static_cast<FormattedBuffer*>(context);
  206. // Invoke the currently configured formatter.
  207. (this->*format)(request, buffer, localTime, first, last);
  208. // We're done.
  209. buffer.endRecord();
  210. }
  211. void LocalFile::Flush(
  212. void* context,
  213. IASTL::IASRequest& request,
  214. const SYSTEMTIME& localTime
  215. )
  216. {
  217. FormattedBuffer& buffer = *static_cast<FormattedBuffer*>(context);
  218. if (!buffer.empty())
  219. {
  220. if (!log.Write(
  221. request.get_Protocol(),
  222. localTime,
  223. buffer.getBuffer(),
  224. buffer.getLength()
  225. ))
  226. {
  227. IASTL::issue_error(HRESULT_FROM_WIN32(ERROR_WRITE_FAULT));
  228. }
  229. }
  230. }
  231. void LocalFile::formatODBCRecord(
  232. IASTL::IASRequest& request,
  233. FormattedBuffer& buffer,
  234. const SYSTEMTIME& localTime,
  235. PATTRIBUTEPOSITION firstPos,
  236. PATTRIBUTEPOSITION lastPos
  237. ) const
  238. {
  239. //////////
  240. // Column 1: Computer name.
  241. //////////
  242. buffer.append('\"');
  243. buffer.append((PBYTE)computerName, computerNameLen);
  244. buffer.append('\"');
  245. //////////
  246. // Column 2: Service name.
  247. //////////
  248. buffer.beginColumn();
  249. switch (request.get_Protocol())
  250. {
  251. case IAS_PROTOCOL_RADIUS:
  252. buffer.append((const BYTE*)"\"IAS\"", 5);
  253. break;
  254. case IAS_PROTOCOL_RAS:
  255. buffer.append((const BYTE*)"\"RAS\"", 5);
  256. break;
  257. }
  258. //////////
  259. // Column 3: Record time.
  260. //////////
  261. buffer.beginColumn();
  262. buffer.appendDate(localTime);
  263. buffer.beginColumn();
  264. buffer.appendTime(localTime);
  265. //////////
  266. // Allocate a blank record.
  267. //////////
  268. PATTRIBUTEPOSITION *firstField, *curField, *lastField;
  269. size_t nfield = schema.getNumFields() + 1;
  270. firstField = STACK_ALLOC(PATTRIBUTEPOSITION, nfield);
  271. memset(firstField, 0, sizeof(PATTRIBUTEPOSITION) * nfield);
  272. lastField = firstField + nfield;
  273. //////////
  274. // Sort the attributes to coalesce multi-valued attributes.
  275. //////////
  276. std::sort(firstPos, lastPos, IASTL::IASOrderByID());
  277. //////////
  278. // Add a null terminator. This will make it easier to handle multi-valued
  279. // attributes.
  280. //////////
  281. lastPos->pAttribute = NULL;
  282. //////////
  283. // Fill in the fields.
  284. //////////
  285. PATTRIBUTEPOSITION curPos;
  286. DWORD lastSeen = (DWORD)-1;
  287. for (curPos = firstPos; curPos != lastPos; ++curPos)
  288. {
  289. // Only process if this is a new attribute type.
  290. if (curPos->pAttribute->dwId != lastSeen)
  291. {
  292. lastSeen = curPos->pAttribute->dwId;
  293. firstField[schema.getOrdinal(lastSeen)] = curPos;
  294. }
  295. }
  296. //////////
  297. // Pack the record into the buffer. We skip field 0, since that's where
  298. // we map all the attributes we don't want to log.
  299. //////////
  300. for (curField = firstField + 1; curField != lastField; ++curField)
  301. {
  302. buffer.beginColumn();
  303. if (*curField) { buffer.append(*curField); }
  304. }
  305. }
  306. void LocalFile::formatW3CRecord(
  307. IASTL::IASRequest& request,
  308. FormattedBuffer& buffer,
  309. const SYSTEMTIME& localTime,
  310. PATTRIBUTEPOSITION firstPos,
  311. PATTRIBUTEPOSITION lastPos
  312. ) const
  313. {
  314. //////////
  315. // Column 1: NAS-IP-Addresses
  316. //////////
  317. PIASATTRIBUTE attr = IASPeekAttribute(
  318. request,
  319. RADIUS_ATTRIBUTE_NAS_IP_ADDRESS,
  320. IASTYPE_INET_ADDR
  321. );
  322. if (attr == 0)
  323. {
  324. attr = IASPeekAttribute(
  325. request,
  326. IAS_ATTRIBUTE_CLIENT_IP_ADDRESS,
  327. IASTYPE_INET_ADDR
  328. );
  329. }
  330. if (attr != 0)
  331. {
  332. buffer.append(attr->Value);
  333. }
  334. //////////
  335. // Column 2: User-Name
  336. //////////
  337. buffer.beginColumn();
  338. attr = IASPeekAttribute(request,
  339. RADIUS_ATTRIBUTE_USER_NAME,
  340. IASTYPE_OCTET_STRING);
  341. if (attr) { buffer.append(attr->Value); }
  342. //////////
  343. // Column 3: Record time.
  344. //////////
  345. buffer.beginColumn();
  346. buffer.appendDate(localTime);
  347. buffer.beginColumn();
  348. buffer.appendTime(localTime);
  349. //////////
  350. // Column 4: Service name.
  351. //////////
  352. buffer.beginColumn();
  353. switch (request.get_Protocol())
  354. {
  355. case IAS_PROTOCOL_RADIUS:
  356. buffer.append("IAS");
  357. break;
  358. case IAS_PROTOCOL_RAS:
  359. buffer.append("RAS");
  360. break;
  361. }
  362. //////////
  363. // Column 5: Computer name.
  364. //////////
  365. buffer.beginColumn();
  366. buffer.append((PBYTE)computerName, computerNameLen);
  367. //////////
  368. // Pack the attributes into the buffer.
  369. //////////
  370. PATTRIBUTEPOSITION curPos;
  371. for (curPos = firstPos; curPos != lastPos; ++curPos)
  372. {
  373. if (!schema.excludeFromLog(curPos->pAttribute->dwId))
  374. {
  375. buffer.beginColumn();
  376. buffer.append(curPos->pAttribute->dwId);
  377. buffer.beginColumn();
  378. buffer.append(*(curPos->pAttribute));
  379. }
  380. }
  381. }