diff --git a/.gitignore b/.gitignore index 1e47279..ebf1813 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /node_modules /dist .DS_Store -*.log \ No newline at end of file +*.log +/shares.json +/users.json \ No newline at end of file diff --git a/demo/index.ts b/demo/index.ts index b7274ea..4fd3723 100644 --- a/demo/index.ts +++ b/demo/index.ts @@ -1,4 +1,4 @@ -// import runServer from "./runServer"; +import runServer from "./runServer"; import runClient from "./runClient"; (async () => { @@ -10,6 +10,6 @@ import runClient from "./runClient"; SHARE: share = "test" } = process.env; - // await runServer(); - await runClient(host, domain, username, password, share); + await runServer(); + // await runClient(host, domain, username, password, share); })(); \ No newline at end of file diff --git a/demo/runServer.ts b/demo/runServer.ts index 1fc8245..acb6698 100644 --- a/demo/runServer.ts +++ b/demo/runServer.ts @@ -1,7 +1,22 @@ -import { Server } from "../src"; +import { + Server, + serverAuthenticationProviders, + serverShareProviders +} from "../src"; + +const { FileAuthenticationProvider } = serverAuthenticationProviders; +const { FileShareProvider } = serverShareProviders; export default async () => { const server = new Server(); + + const authenticationProvider = new FileAuthenticationProvider(`./users.json`); + server.use(authenticationProvider); + + const shareProvider = new FileShareProvider(`./shares.json`); + server.use(shareProvider); + + await server.init(); await server.listen(); - console.log("server started", server.port); + console.log("server started on port", server.port); }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3872381..1e2363e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,29 +5,66 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.10.4" } }, "@babel/helper-validator-identifier": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", - "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.0", + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } } }, "@sindresorhus/is": { @@ -46,9 +83,9 @@ } }, "@types/node": { - "version": "13.7.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.4.tgz", - "integrity": "sha512-oVeL12C6gQS/GAExndigSaLxTrKpQPxewx9bOcwfvJiJge4rr7wNaph4J+ns5hrmIV2as5qxqN8YKthn9qh0jw==", + "version": "13.13.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.23.tgz", + "integrity": "sha512-L31WmMJYKb15PDqFWutn8HNwrNK6CE6bkWgSB0dO1XpNoHrszVKV1Clcnfgd6c/oG54TVF8XQEvY2gQrW8K6Mw==", "dev": true }, "abbrev": { @@ -86,12 +123,12 @@ "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "anymatch": { @@ -145,57 +182,6 @@ "term-size": "^2.1.0", "type-fest": "^0.8.1", "widest-line": "^3.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "brace-expansion": { @@ -268,14 +254,30 @@ "dev": true }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "chokidar": { @@ -316,18 +318,18 @@ } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "commander": { @@ -407,6 +409,14 @@ "is-obj": "^2.0.0" } }, + "dtyp": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dtyp/-/dtyp-0.1.2.tgz", + "integrity": "sha512-0q5FJ7BPNW4W8V+IGdZqdgVWCXCTdUkunpXye4X7lQUZujzMQVA8PPOkHtkhk4mGnakfhHeixWX045mHfwfLQg==", + "requires": { + "moment-timezone": "^0.5.31" + } + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -684,9 +694,9 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -768,18 +778,18 @@ "dev": true }, "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { "minimist": "^1.2.5" } }, "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "moment-timezone": { "version": "0.5.31", @@ -834,6 +844,14 @@ "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", "dev": true }, + "ntlmv2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/ntlmv2/-/ntlmv2-0.1.4.tgz", + "integrity": "sha512-DiVmUsFt3LlFwEo6VY5pTTab6Jxs3P5HYGj1rk+2FAGEgVyfs0mJNdW4JjAkEuIWmG6S49qrc1aHkxyzw9zNuw==", + "requires": { + "dtyp": "^0.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -958,9 +976,9 @@ } }, "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -1134,15 +1152,15 @@ } }, "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz", + "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==", "dev": true }, "tslint": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.0.tgz", - "integrity": "sha512-fXjYd/61vU6da04E505OZQGb2VCN2Mq3doeWcOIryuG+eqdmFUXTYVwdhnbEu2k46LNLgUYt9bI5icQze/j0bQ==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -1153,11 +1171,48 @@ "glob": "^7.1.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", + "mkdirp": "^0.5.3", "resolve": "^1.3.2", "semver": "^5.3.0", - "tslib": "^1.10.0", + "tslib": "^1.13.0", "tsutils": "^2.29.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } } }, "tsutils": { @@ -1244,57 +1299,6 @@ "pupa": "^2.0.1", "semver-diff": "^3.1.1", "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "url-parse-lax": { diff --git a/package.json b/package.json index d89f2b7..24883a2 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,9 @@ "node": ">=10.0.0" }, "dependencies": { - "moment-timezone": "^0.5.31" + "dtyp": "^0.1.2", + "moment-timezone": "^0.5.31", + "ntlmv2": "^0.1.4" }, "devDependencies": { "@types/node": "^13.7.4", diff --git a/src/client/Client.ts b/src/client/Client.ts index e404052..a3f3353 100644 --- a/src/client/Client.ts +++ b/src/client/Client.ts @@ -1,14 +1,14 @@ import crypto from "crypto"; import { Socket } from "net"; import { EventEmitter } from "events"; -import Packet from "../protocol/Packet"; -import Request from "../protocol/smb2/Request"; -import Response from "../protocol/smb2/Response"; -import Header from "../protocol/smb2/Header"; -import StatusCode from "../protocol/smb2/StatusCode"; -import Smb2PacketType from "../protocol/smb2/PacketType"; +import Packet from "../protocols/Packet"; +import Header from "../protocols/smb2/Header"; +import Request from "../protocols/smb2/Request"; +import Response from "../protocols/smb2/Response"; +import StatusCode from "../protocols/smb2/StatusCode"; import Session, { AuthenticateOptions } from "./Session"; -import * as structureUtil from "../protocol/structureUtil"; +import Smb2PacketType from "../protocols/smb2/PacketType"; +import * as structureUtil from "../protocols/structureUtil"; export interface Options { port?: number; diff --git a/src/client/Directory.ts b/src/client/Directory.ts index 649e59c..466c321 100644 --- a/src/client/Directory.ts +++ b/src/client/Directory.ts @@ -1,18 +1,18 @@ import Tree from "./Tree"; import { EventEmitter } from "events"; -import * as util from "../protocol/util"; -import Response from "../protocol/smb2/Response"; -import StatusCode from "../protocol/smb2/StatusCode"; -import PacketType from "../protocol/smb2/PacketType"; -import FileAttribute from "../protocol/smb2/FileAttribute"; -import ShareAccessType from "../protocol/smb2/ShareAccessType"; -import DirectoryAccess from "../protocol/smb2/DirectoryAccess"; -import { CreateOptions } from "../protocol/smb2/packets/Create"; -import * as structureUtil from "../protocol/structureUtil"; -import DirectoryEntry from "../protocol/models/DirectoryEntry"; -import { InfoType, FileInfoClass } from "../protocol/smb2/packets/SetInfo"; -import CreateDispositionType from "../protocol/smb2/CreateDispositionType"; -import { Flags as ChangeNotifyFlags } from "../protocol/smb2/packets/ChangeNotify"; +import * as util from "../protocols/util"; +import Response from "../protocols/smb2/Response"; +import StatusCode from "../protocols/smb2/StatusCode"; +import PacketType from "../protocols/smb2/PacketType"; +import FileAttribute from "../protocols/smb2/FileAttribute"; +import ShareAccessType from "../protocols/smb2/ShareAccessType"; +import DirectoryAccess from "../protocols/smb2/DirectoryAccess"; +import { CreateOptions } from "../protocols/smb2/packets/Create"; +import * as structureUtil from "../protocols/structureUtil"; +import DirectoryEntry from "../models/DirectoryEntry"; +import { InfoType, FileInfoClass } from "../protocols/smb2/packets/SetInfo"; +import CreateDispositionType from "../protocols/smb2/CreateDispositionType"; +import { Flags as ChangeNotifyFlags } from "../protocols/smb2/packets/ChangeNotify"; interface OpenOptions { desiredAccess?: DirectoryAccess; diff --git a/src/client/File.ts b/src/client/File.ts index ad7d0ea..cf1a7f9 100644 --- a/src/client/File.ts +++ b/src/client/File.ts @@ -1,14 +1,14 @@ import Tree from "./Tree"; import { EventEmitter } from "events"; -import * as util from "../protocol/util"; -import StatusCode from "../protocol/smb2/StatusCode"; -import PacketType from "../protocol/smb2/PacketType"; -import FileAttribute from "../protocol/smb2/FileAttribute"; -import ShareAccessType from "../protocol/smb2/ShareAccessType"; -import { CreateOptions } from "../protocol/smb2/packets/Create"; -import CreateDispositionType from "../protocol/smb2/CreateDispositionType"; -import FilePipePrinterAccess from "../protocol/smb2/FilePipePrinterAccess"; -import { FileInfoClass, InfoType } from "../protocol/smb2/packets/SetInfo"; +import * as util from "../protocols/util"; +import StatusCode from "../protocols/smb2/StatusCode"; +import PacketType from "../protocols/smb2/PacketType"; +import FileAttribute from "../protocols/smb2/FileAttribute"; +import ShareAccessType from "../protocols/smb2/ShareAccessType"; +import { CreateOptions } from "../protocols/smb2/packets/Create"; +import CreateDispositionType from "../protocols/smb2/CreateDispositionType"; +import FilePipePrinterAccess from "../protocols/smb2/FilePipePrinterAccess"; +import { FileInfoClass, InfoType } from "../protocols/smb2/packets/SetInfo"; const maxReadChunkLength = 0x00010000; const maxWriteChunkLength = 0x00010000 - 0x71; diff --git a/src/client/Session.ts b/src/client/Session.ts index 1e410a1..43c82bc 100644 --- a/src/client/Session.ts +++ b/src/client/Session.ts @@ -1,10 +1,10 @@ import Tree from "./Tree"; +import ntlmv2 from "ntlmv2"; import Client from "./Client"; import { EventEmitter } from "events"; -import Dialect from "../protocol/smb2/Dialect"; -import Header from "../protocol/smb2/Header"; -import * as ntlmUtil from "../protocol/ntlm/util"; -import PacketType from "../protocol/smb2/PacketType"; +import Dialect from "../protocols/smb2/Dialect"; +import Header from "../protocols/smb2/Header"; +import PacketType from "../protocols/smb2/PacketType"; export interface AuthenticateOptions { domain: string; @@ -68,19 +68,20 @@ class Session extends EventEmitter { }); const sessionSetupResponse = await this.request( { type: PacketType.SessionSetup }, - { buffer: ntlmUtil.encodeNegotiationMessage(this.client.host, options.domain) } + { buffer: ntlmv2.serializeNegotiationMessage(options.domain, this.client.host) } ); this._id = sessionSetupResponse.header.sessionId; - const nonce = ntlmUtil.decodeChallengeMessage(sessionSetupResponse.body.buffer as Buffer); + const challengeMessage = ntlmv2.parseChallengeMessage(sessionSetupResponse.body.buffer as Buffer); + await this.request( { type: PacketType.SessionSetup }, { - buffer: ntlmUtil.encodeAuthenticationMessage( + buffer: ntlmv2.serializeAuthenticationMessage( options.username, this.client.host, options.domain, - nonce, + challengeMessage.serverChallenge, options.password ) } @@ -103,7 +104,7 @@ class Session extends EventEmitter { await Promise.all(this.connectedTrees.map(x => x.disconnect())); - await this.request({ type: PacketType.LogOff }); + await this.request({ type: PacketType.SessionLogoff }); delete this._id; this.emit("logoff", this); diff --git a/src/client/Tree.ts b/src/client/Tree.ts index 28a869e..e52c6b0 100644 --- a/src/client/Tree.ts +++ b/src/client/Tree.ts @@ -2,12 +2,12 @@ import File from "./File"; import Session from "./Session"; import Directory from "./Directory"; import { EventEmitter } from "events"; -import Header from "../protocol/smb2/Header"; -import * as util from "../protocol/util"; -import Response from "../protocol/smb2/Response"; -import PacketType from "../protocol/smb2/PacketType"; -import DirectoryAccess from "../protocol/smb2/DirectoryAccess"; -import FilePipePrinterAccess from "../protocol/smb2/FilePipePrinterAccess"; +import Header from "../protocols/smb2/Header"; +import * as util from "../protocols/util"; +import Response from "../protocols/smb2/Response"; +import PacketType from "../protocols/smb2/PacketType"; +import DirectoryAccess from "../protocols/smb2/DirectoryAccess"; +import FilePipePrinterAccess from "../protocols/smb2/FilePipePrinterAccess"; interface Tree { on(event: "connect" | "disconnect", callback: (tree: Tree) => void): this; diff --git a/src/index.ts b/src/index.ts index 4f408a4..a42131f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,33 @@ import { default as Client } from "./client/Client"; -// import { default as Server } from "./server/Server"; + +import { default as Server } from "./server/Server"; + +import ServerAuthenticationProvider from "./server/AuthenticationProvider"; +import * as serverAuthenticationProviders from "./server/authenticationProviders"; + +import ServerShareProvider from "./server/ShareProvider"; +import * as serverShareProviders from "./server/shareProviders"; export { Client, - // Server + + Server, + + ServerAuthenticationProvider, + serverAuthenticationProviders, + + ServerShareProvider, + serverShareProviders }; export default { Client, - // Server + + Server, + + ServerAuthenticationProvider, + serverAuthenticationProviders, + + ServerShareProvider, + serverShareProviders }; \ No newline at end of file diff --git a/src/protocol/models/ChangeEntry.ts b/src/models/ChangeEntry.ts similarity index 65% rename from src/protocol/models/ChangeEntry.ts rename to src/models/ChangeEntry.ts index 8349047..7481cfb 100644 --- a/src/protocol/models/ChangeEntry.ts +++ b/src/models/ChangeEntry.ts @@ -1,4 +1,4 @@ -import FileAction from "../smb2/FileAction"; +import FileAction from "../protocols/smb2/FileAction"; export default interface ChangeEntry { action: FileAction; diff --git a/src/protocol/models/DirectoryEntry.ts b/src/models/DirectoryEntry.ts similarity index 100% rename from src/protocol/models/DirectoryEntry.ts rename to src/models/DirectoryEntry.ts diff --git a/src/protocol/ntlm/NegotiateFlag.ts b/src/protocol/ntlm/NegotiateFlag.ts deleted file mode 100644 index fda3cde..0000000 --- a/src/protocol/ntlm/NegotiateFlag.ts +++ /dev/null @@ -1,35 +0,0 @@ -enum NegotiateFlag { - UnicodeEncoding = 1 << 0, - OemEncoding = 1 << 1, - TargetNameSupplied = 1 << 2, - - Sign = 1 << 4, - Seal = 1 << 5, - Diagram = 1 << 6, - - LanManagerSessionKey = 1 << 7, - NTLMSessionSecurity = 1 << 9, - - Anonymous = 1 << 11, - - DomainNameSupplied = 1 << 12, - WorkstationNameSupplied = 1 << 13, - - AlwaysSign = 1 << 15, - TargetTypeDomain = 1 << 16, - TargetTypeServer = 1 << 17, - - ExtendedSessionSecurity = 1 << 19, - Identify = 1 << 20, - - RequestNonNtSessionKey = 1 << 22, - TargetInfo = 1 << 23, - - Version = 1 << 25, - - Use128BitEncryption = 1 << 29, - KeyExchange = 1 << 30, - Use56BitEncryption = 1 << 31 -} - -export default NegotiateFlag; \ No newline at end of file diff --git a/src/protocol/ntlm/util.ts b/src/protocol/ntlm/util.ts deleted file mode 100644 index 4a610df..0000000 --- a/src/protocol/ntlm/util.ts +++ /dev/null @@ -1,333 +0,0 @@ -import crypto from "crypto"; -import NegotiateFlag from "./NegotiateFlag"; - -export const encodeNegotiationMessage = (hostname: string, domain: string) => { - hostname = hostname.toUpperCase(); - domain = domain.toUpperCase(); - - const hostnameLength = Buffer.byteLength(hostname, "ascii"); - const domainLength = Buffer.byteLength(domain, "ascii"); - - let offset = 0; - const buffer = Buffer.alloc(32 + hostnameLength + domainLength); - - buffer.write("NTLMSSP", offset, 7, "ascii"); - offset += 7; - buffer.writeUInt8(0, offset); - offset += 1; - - buffer.writeUInt32LE(1, offset); - offset += 4; - - const negotiateFlags = NegotiateFlag.UnicodeEncoding | NegotiateFlag.NTLMSessionSecurity | NegotiateFlag.AlwaysSign; - buffer.writeUInt32LE(negotiateFlags, offset); - offset += 4; - - buffer.writeUInt16LE(domainLength, offset); - offset += 2; - buffer.writeUInt16LE(domainLength, offset); - offset += 2; - - const domainOffset = 0x20 + hostnameLength; - buffer.writeUInt32LE(domainOffset, offset); - offset += 4; - - buffer.writeUInt16LE(hostnameLength, offset); - offset += 2; - buffer.writeUInt16LE(hostnameLength, offset); - offset += 2; - - buffer.writeUInt32LE(0x20, offset); - offset += 4; - - buffer.write(hostname, 0x20, hostnameLength, "ascii"); - buffer.write(domain, domainOffset, domainLength, "ascii"); - - return buffer; -}; - -export const decodeNegotiationMessage = (buffer: Buffer) => { - let offset = 0; - - const protocol = buffer.slice(0, 7).toString("ascii"); - if ( - protocol !== "NTLMSSP" || - buffer.readInt8(7) !== 0x00 - ) throw new Error("ntlmssp_header_not_found"); - offset += 8; - - const type = buffer.readUInt32LE(offset); - if (type !== 0x01) throw new Error("ntlmssp_type_is_not_one"); - offset += 4; - - const negotiateFlags = buffer.readUInt32LE(offset); - offset += 4; - - const domainLength = buffer.readUInt16LE(offset); - offset += 2; - const domainMaxLength = buffer.readUInt16LE(offset); - offset += 2; - const domainOffset = buffer.readUInt32LE(offset); - offset += 4; - - const hostnameLength = buffer.readUInt16LE(offset); - offset += 2; - const hostnameMaxLength = buffer.readUInt16LE(offset); - offset += 2; - const hostnameOffset = buffer.readUInt32LE(offset); - offset += 4; - - const domain = buffer.slice(domainOffset, domainOffset + domainLength).toString("ascii"); - const hostname = buffer.slice(hostnameOffset, hostnameOffset + hostnameLength).toString("ascii"); - - return { - negotiateFlags, - domain, - hostname - }; -}; - -export const encodeChallengeMessage = (negotiateFlags: number) => { - let offset = 0; - const buffer = Buffer.alloc(64); - - buffer.write("NTLMSSP", offset, 7, "ascii"); - offset += 7; - buffer.writeUInt8(0, offset); - offset += 1; - - buffer.writeUInt32LE(2, offset); - offset += 4; - - buffer.writeUInt16LE(0, offset); - offset += 2; - - buffer.writeUInt16LE(0, offset); - offset += 2; - - buffer.writeUInt32LE(0, offset); - offset += 4; - - buffer.writeUInt32LE(negotiateFlags, offset); - offset += 4; - - generateServerChallenge().copy(buffer, offset); - offset += 8; - - buffer.fill(0, offset, offset + 8); - offset += 8; - - return buffer; -}; - -export const decodeChallengeMessage = (buffer: Buffer) => { - let offset = 0; - - const protocol = buffer.slice(0, 7).toString("ascii"); - if ( - protocol !== "NTLMSSP" || - buffer.readInt8(7) !== 0x00 - ) throw new Error("ntlmssp_header_not_found"); - offset += 8; - - const type = buffer.readUInt32LE(offset); - if (type !== 0x02) throw new Error("ntlmssp_type_is_not_two"); - offset += 4; - - const targetNameLength = buffer.readUInt16LE(offset); - offset += 2; - - const targetNameMaxLength = buffer.readUInt16LE(offset); - offset += 2; - - const targetNameOffset = buffer.readUInt32LE(offset); - offset += 4; - - const negotiateFlags = buffer.readUInt32LE(offset); - offset += 4; - - const serverChallenge = buffer.slice(offset, offset + 8); - offset += 8; - - offset += 8; // Reserved - - return serverChallenge; -} - -export const encodeAuthenticationMessage = (username: string, hostname: string, domain: string, nonce: Buffer, password: string) => { - hostname = hostname.toUpperCase(); - domain = domain.toUpperCase(); - - const lmHash = Buffer.alloc(21); - createLmHash(password).copy(lmHash); - lmHash.fill(0x00, 16); - - const ntHash = Buffer.alloc(21); - createNtHash(password).copy(ntHash); - ntHash.fill(0x00, 16); - - const lmResponse = createResponse(lmHash, nonce); - const ntResponse = createResponse(ntHash, nonce); - - const usernameLength = Buffer.byteLength(username, "ucs2"); - const hostnameLength = Buffer.byteLength(hostname, "ucs2"); - const domainLength = Buffer.byteLength(domain, "ucs2"); - const lmResponseLength = 0x18; - const ntResponseLength = 0x18; - - const domainOffset = 0x40; - const usernameOffset = domainOffset + domainLength; - const hostnameOffset = usernameOffset + usernameLength; - const lmResponseOffset = hostnameOffset + hostnameLength; - const ntResponseOffset = lmResponseOffset + lmResponseLength; - - let offset = 0; - const messageLength = 64 + domainLength + usernameLength + hostnameLength + lmResponseLength + ntResponseLength; - const buffer = Buffer.alloc(messageLength); - - buffer.write("NTLMSSP", offset, 7, "ascii"); // byte protocol[8]; - offset += 7; - buffer.writeUInt8(0, offset); - offset++; - - buffer.writeUInt8(0x03, offset); // byte type; - offset++; - - buffer.fill(0x00, offset, offset + 3); // byte zero[3]; - offset += 3; - - buffer.writeUInt16LE(lmResponseLength, offset); // short lm_resp_len; - offset += 2; - buffer.writeUInt16LE(lmResponseLength, offset); // short lm_resp_len; - offset += 2; - buffer.writeUInt16LE(lmResponseOffset, offset); // short lm_resp_off; - offset += 2; - buffer.fill(0x00, offset, offset + 2); // byte zero[2]; - offset += 2; - - buffer.writeUInt16LE(ntResponseLength, offset); // short nt_resp_len; - offset += 2; - buffer.writeUInt16LE(ntResponseLength, offset); // short nt_resp_len; - offset += 2; - buffer.writeUInt16LE(ntResponseOffset, offset); // short nt_resp_off; - offset += 2; - buffer.fill(0x00, offset, offset + 2); // byte zero[2]; - offset += 2; - - buffer.writeUInt16LE(domainLength, offset); // short dom_len; - offset += 2; - buffer.writeUInt16LE(domainLength, offset); // short dom_len; - offset += 2; - buffer.writeUInt16LE(domainOffset, offset); // short dom_off; - offset += 2; - buffer.fill(0x00, offset, offset + 2); // byte zero[2]; - offset += 2; - - buffer.writeUInt16LE(usernameLength, offset); // short user_len; - offset += 2; - buffer.writeUInt16LE(usernameLength, offset); // short user_len; - offset += 2; - buffer.writeUInt16LE(usernameOffset, offset); // short user_off; - offset += 2; - buffer.fill(0x00, offset, offset + 2); // byte zero[2]; - offset += 2; - - buffer.writeUInt16LE(hostnameLength, offset); // short host_len; - offset += 2; - buffer.writeUInt16LE(hostnameLength, offset); // short host_len; - offset += 2; - buffer.writeUInt16LE(hostnameOffset, offset); // short host_off; - offset += 2; - buffer.fill(0x00, offset, offset + 6); // byte zero[6]; - offset += 6; - - buffer.writeUInt16LE(messageLength, offset); // short msg_len; - offset += 2; - buffer.fill(0x00, offset, offset + 2); // byte zero[2]; - offset += 2; - - const negotiateFlags = NegotiateFlag.UnicodeEncoding | NegotiateFlag.NTLMSessionSecurity | NegotiateFlag.AlwaysSign; - buffer.writeUInt32LE(negotiateFlags, offset); - offset += 4; - - buffer.write(domain, domainOffset, domainLength, "ucs2"); - buffer.write(username, usernameOffset, usernameLength, "ucs2"); - buffer.write(hostname, hostnameOffset, hostnameLength, "ucs2"); - lmResponse.copy(buffer, lmResponseOffset, 0, lmResponseLength); - ntResponse.copy(buffer, ntResponseOffset, 0, ntResponseLength); - - return buffer; -}; - -export const generateServerChallenge = () => { - return crypto.randomBytes(8); -}; - -const fixOddParity = (buffer: Buffer) => { - for (let index = 0; index < buffer.length; index++) { - let parity = 1; - for (let index2 = 1; index2 < 8; index2++) { - parity = (parity + ((buffer[index] >> index2) & 1)) % 2; - } - buffer[index] |= parity & 1; - } - return buffer; -}; - -const createDESKey = (key56: Buffer) => { - const key64 = Buffer.alloc(8); - - key64[0] = key56[0] & 0xFE; - key64[1] = ((key56[0] << 7) & 0xFF) | (key56[1] >> 1); - key64[2] = ((key56[1] << 6) & 0xFF) | (key56[2] >> 2); - key64[3] = ((key56[2] << 5) & 0xFF) | (key56[3] >> 3); - key64[4] = ((key56[3] << 4) & 0xFF) | (key56[4] >> 4); - key64[5] = ((key56[4] << 3) & 0xFF) | (key56[5] >> 5); - key64[6] = ((key56[5] << 2) & 0xFF) | (key56[6] >> 6); - key64[7] = (key56[6] << 1) & 0xFF; - - return key64; -}; - -const createLmHash = (text: string) => { - const upperCaseText = text.substring(0, 14).toUpperCase(); - const upperCaseTextLength = Buffer.byteLength(upperCaseText, "ascii"); - - const paddingBuffer = Buffer.alloc(14); - paddingBuffer.write(upperCaseText, 0, upperCaseTextLength, "ascii"); - paddingBuffer.fill(0, upperCaseTextLength); - - const halves = [ - fixOddParity(createDESKey(paddingBuffer.slice(0, 7))), - fixOddParity(createDESKey(paddingBuffer.slice(7, 14))) - ]; - - const buffer = Buffer.alloc(16); - let offset = 0; - for (const halve of halves) { - const cipher = crypto.createCipheriv("DES-ECB", halve, ""); - const string = cipher.update("KGS!@#$%", "binary", "binary"); - buffer.write(string, offset, offset + 8, "binary"); - offset += 8; - } - - return buffer; -}; - -const createNtHash = (str: string) => { - const ucs2 = Buffer.from(str, "ucs2"); - const md4 = crypto.createHash("md4"); - md4.update(ucs2); - return Buffer.from(md4.digest("hex"), "hex"); -}; - -const createResponse = (hash: Buffer, nonce: Buffer) => { - const buffer = Buffer.alloc(24); - for (let index = 0; index < 3; index++) { - const keyBuffer = fixOddParity(createDESKey(hash.slice(index * 7, index * 7 + 7))); - const cipher = crypto.createCipheriv("DES-ECB", keyBuffer, ""); - const string = cipher.update(nonce.toString("binary"), "binary", "binary"); - buffer.write(string, index * 8, index * 8 + 8, "binary"); - } - return buffer; -}; \ No newline at end of file diff --git a/src/protocol/smb2/Capability.ts b/src/protocol/smb2/Capability.ts deleted file mode 100644 index 1b48003..0000000 --- a/src/protocol/smb2/Capability.ts +++ /dev/null @@ -1,6 +0,0 @@ -enum Capability { - DistributedFileSystem = 0x00000001, - MultiCreditSupport = 0x00000004 -} - -export default Capability; \ No newline at end of file diff --git a/src/protocol/Packet.ts b/src/protocols/Packet.ts similarity index 91% rename from src/protocol/Packet.ts rename to src/protocols/Packet.ts index 75e1bb9..0f9979d 100644 --- a/src/protocol/Packet.ts +++ b/src/protocols/Packet.ts @@ -1,4 +1,4 @@ -import * as protocolIds from "./protocolIds"; +import * as ProtocolIds from "./ProtocolIds"; import StructureField from "./StructureField"; import * as structureUtil from "./structureUtil"; @@ -6,7 +6,7 @@ export const protocolIdStructureField: StructureField = { type: String, encoding: "hex", size: 4, - defaultValue: protocolIds.smb2 + defaultValue: ProtocolIds.Smb2 }; export default class Packet { diff --git a/src/protocol/protocolIds.ts b/src/protocols/ProtocolIds.ts similarity index 81% rename from src/protocol/protocolIds.ts rename to src/protocols/ProtocolIds.ts index 3442d5d..adb68f2 100644 --- a/src/protocol/protocolIds.ts +++ b/src/protocols/ProtocolIds.ts @@ -1,4 +1,4 @@ -export const smb = Buffer +export const Smb = Buffer .from([ 0xff, "S".charCodeAt(0), @@ -7,7 +7,7 @@ export const smb = Buffer ]) .toString("hex"); -export const smb2 = Buffer +export const Smb2 = Buffer .from([ 0xfe, "S".charCodeAt(0), diff --git a/src/protocol/Request.ts b/src/protocols/Request.ts similarity index 100% rename from src/protocol/Request.ts rename to src/protocols/Request.ts diff --git a/src/protocol/Response.ts b/src/protocols/Response.ts similarity index 100% rename from src/protocol/Response.ts rename to src/protocols/Response.ts diff --git a/src/protocol/Structure.ts b/src/protocols/Structure.ts similarity index 100% rename from src/protocol/Structure.ts rename to src/protocols/Structure.ts diff --git a/src/protocol/StructureField.ts b/src/protocols/StructureField.ts similarity index 92% rename from src/protocol/StructureField.ts rename to src/protocols/StructureField.ts index 41e27fe..df1a256 100644 --- a/src/protocol/StructureField.ts +++ b/src/protocols/StructureField.ts @@ -9,6 +9,7 @@ type StructureField = { count?: number; sizeFieldName?: string; size?: number; + offsetFieldName?: string; defaultValue?: Value; }; diff --git a/src/protocol/Value.ts b/src/protocols/Value.ts similarity index 100% rename from src/protocol/Value.ts rename to src/protocols/Value.ts diff --git a/src/protocol/smb/Header.ts b/src/protocols/smb/Header.ts similarity index 100% rename from src/protocol/smb/Header.ts rename to src/protocols/smb/Header.ts diff --git a/src/protocol/smb/Packet.ts b/src/protocols/smb/Packet.ts similarity index 100% rename from src/protocol/smb/Packet.ts rename to src/protocols/smb/Packet.ts diff --git a/src/protocol/smb/PacketType.ts b/src/protocols/smb/PacketType.ts similarity index 100% rename from src/protocol/smb/PacketType.ts rename to src/protocols/smb/PacketType.ts diff --git a/src/protocol/smb/Request.ts b/src/protocols/smb/Request.ts similarity index 86% rename from src/protocol/smb/Request.ts rename to src/protocols/smb/Request.ts index 022d3a5..425ea86 100644 --- a/src/protocol/smb/Request.ts +++ b/src/protocols/smb/Request.ts @@ -12,6 +12,5 @@ export default class Request extends ProtocolRequest { serialize() { return Buffer.from([]); - // return Packet.serialize(this.header, this.body); } } \ No newline at end of file diff --git a/src/protocol/smb/Response.ts b/src/protocols/smb/Response.ts similarity index 86% rename from src/protocol/smb/Response.ts rename to src/protocols/smb/Response.ts index eee4c46..c180f68 100644 --- a/src/protocol/smb/Response.ts +++ b/src/protocols/smb/Response.ts @@ -12,6 +12,5 @@ export default class Response extends ProtocolResponse { serialize() { return Buffer.from([]); - // return Packet.serialize(this.header, this.body); } } \ No newline at end of file diff --git a/src/protocols/smb2/Capability.ts b/src/protocols/smb2/Capability.ts new file mode 100644 index 0000000..24c7f94 --- /dev/null +++ b/src/protocols/smb2/Capability.ts @@ -0,0 +1,11 @@ +enum Capability { + DistributedFileSystem = 1 << 0, + Leasing = 1 << 1, + LargeMtu = 1 << 2, + MultiChannel = 1 << 3, + PersistentHandles = 1 << 4, + DirectoryLeasing = 1 << 5, + Encryption = 1 << 6 +} + +export default Capability; \ No newline at end of file diff --git a/src/protocols/smb2/ControlCode.ts b/src/protocols/smb2/ControlCode.ts new file mode 100644 index 0000000..7364299 --- /dev/null +++ b/src/protocols/smb2/ControlCode.ts @@ -0,0 +1,19 @@ +enum ControlCode { + DfsGetReferrals = 0x00060194, + PipeSeek = 0x0011400C, + PipeWait = 0x00110018, + PipeTransceive = 0x0011C017, + SrvCopyChunk = 0x001440F2, + SrvEnumerateSnapshots = 0x00144064, + SrvRequestResumeKey = 0x00140078, + SrvReadHash = 0x001441bb, + SrvCopyChunkWrite = 0x001480F2, + LmrRequestResiliency = 0x001401D4, + QueryNetworkInterfaceInfo = 0x001401FC, + SetReparsePoint = 0x000900A4, + DfsGetReferralsEx = 0x000601B0, + FileLevelTrim = 0x00098208, + ValidateNegotiateInfo = 0x00140204 +} + +export default ControlCode; \ No newline at end of file diff --git a/src/protocol/smb2/CreateDispositionType.ts b/src/protocols/smb2/CreateDispositionType.ts similarity index 100% rename from src/protocol/smb2/CreateDispositionType.ts rename to src/protocols/smb2/CreateDispositionType.ts diff --git a/src/protocol/smb2/Dialect.ts b/src/protocols/smb2/Dialect.ts similarity index 68% rename from src/protocol/smb2/Dialect.ts rename to src/protocols/smb2/Dialect.ts index 1c7e74c..daec5ac 100644 --- a/src/protocol/smb2/Dialect.ts +++ b/src/protocols/smb2/Dialect.ts @@ -1,3 +1,5 @@ +export const formatDialectName = (dialect: Dialect) => dialect.toString(16); + enum Dialect { Smb202 = 0x0202, Smb210 = 0x0210, diff --git a/src/protocol/smb2/DirectoryAccess.ts b/src/protocols/smb2/DirectoryAccess.ts similarity index 100% rename from src/protocol/smb2/DirectoryAccess.ts rename to src/protocols/smb2/DirectoryAccess.ts diff --git a/src/protocol/smb2/FileAction.ts b/src/protocols/smb2/FileAction.ts similarity index 100% rename from src/protocol/smb2/FileAction.ts rename to src/protocols/smb2/FileAction.ts diff --git a/src/protocol/smb2/FileAttribute.ts b/src/protocols/smb2/FileAttribute.ts similarity index 100% rename from src/protocol/smb2/FileAttribute.ts rename to src/protocols/smb2/FileAttribute.ts diff --git a/src/protocol/smb2/FilePipePrinterAccess.ts b/src/protocols/smb2/FilePipePrinterAccess.ts similarity index 92% rename from src/protocol/smb2/FilePipePrinterAccess.ts rename to src/protocols/smb2/FilePipePrinterAccess.ts index 737c0cc..e027787 100644 --- a/src/protocol/smb2/FilePipePrinterAccess.ts +++ b/src/protocols/smb2/FilePipePrinterAccess.ts @@ -4,8 +4,8 @@ enum FilePipePrinterAccess { AppendData = 1 << 2, ReadEa = 1 << 3, WriteEa = 1 << 4, - DeleteChild = 1 << 5, - Execute = 1 << 6, + Execute = 1 << 5, + DeleteChild = 1 << 6, ReadAttributes = 1 << 7, WriteAttributes = 1 << 8, Delete = 1 << 16, diff --git a/src/protocol/smb2/Header.ts b/src/protocols/smb2/Header.ts similarity index 95% rename from src/protocol/smb2/Header.ts rename to src/protocols/smb2/Header.ts index cbcd981..10c0ae8 100644 --- a/src/protocol/smb2/Header.ts +++ b/src/protocols/smb2/Header.ts @@ -85,7 +85,8 @@ export const headerStructure: HeaderStructure = { size: 4 }, treeId: { - type: Number, + type: String, + encoding: "hex", size: 4 }, sessionId: { @@ -94,7 +95,8 @@ export const headerStructure: HeaderStructure = { size: 8 }, signature: { - type: Number, + type: String, + encoding: "hex", size: 16 } }; \ No newline at end of file diff --git a/src/protocol/smb2/HeaderFlag.ts b/src/protocols/smb2/HeaderFlag.ts similarity index 100% rename from src/protocol/smb2/HeaderFlag.ts rename to src/protocols/smb2/HeaderFlag.ts diff --git a/src/protocol/smb2/Packet.ts b/src/protocols/smb2/Packet.ts similarity index 96% rename from src/protocol/smb2/Packet.ts rename to src/protocols/smb2/Packet.ts index b98fe0f..b80868e 100644 --- a/src/protocol/smb2/Packet.ts +++ b/src/protocols/smb2/Packet.ts @@ -32,7 +32,7 @@ export default class Packet { const structure = Packet.getStructure(header); const headerBuffer = Packet.serializeHeader(header); - const bodyBuffer = structureUtil.serializeStructure(structure, body); + const bodyBuffer = structureUtil.serializeStructure(structure, body, { addOffset: headerSize }); const buffer = Buffer.concat([headerBuffer, bodyBuffer]); const prefixedBuffer = Buffer.allocUnsafe(buffer.length + 4); @@ -51,7 +51,7 @@ export default class Packet { } = Packet.parseHeader(buffer); const structure = Packet.getStructure(header); - const body = structureUtil.parseStructure(bodyBuffer, structure); + const body = structureUtil.parseStructure(bodyBuffer, structure, { subtractOffset: headerSize }); return { header, diff --git a/src/protocol/smb2/PacketType.ts b/src/protocols/smb2/PacketType.ts similarity index 94% rename from src/protocol/smb2/PacketType.ts rename to src/protocols/smb2/PacketType.ts index abe0eac..8be56d8 100644 --- a/src/protocol/smb2/PacketType.ts +++ b/src/protocols/smb2/PacketType.ts @@ -1,7 +1,7 @@ enum PacketType { Negotiate = 0x0000, SessionSetup = 0x0001, - LogOff = 0x0002, + SessionLogoff = 0x0002, TreeConnect = 0x0003, TreeDisconnect = 0x0004, Create = 0x0005, diff --git a/src/protocol/smb2/Request.ts b/src/protocols/smb2/Request.ts similarity index 100% rename from src/protocol/smb2/Request.ts rename to src/protocols/smb2/Request.ts diff --git a/src/protocol/smb2/Response.ts b/src/protocols/smb2/Response.ts similarity index 100% rename from src/protocol/smb2/Response.ts rename to src/protocols/smb2/Response.ts diff --git a/src/protocols/smb2/SecurityMode.ts b/src/protocols/smb2/SecurityMode.ts new file mode 100644 index 0000000..77fb73a --- /dev/null +++ b/src/protocols/smb2/SecurityMode.ts @@ -0,0 +1,6 @@ +enum SecurityMode { + SigningEnabled = 1 << 0, + SigningRequired = 1 << 1 +} + +export default SecurityMode; \ No newline at end of file diff --git a/src/protocols/smb2/SessionFlag.ts b/src/protocols/smb2/SessionFlag.ts new file mode 100644 index 0000000..7aec22e --- /dev/null +++ b/src/protocols/smb2/SessionFlag.ts @@ -0,0 +1,7 @@ +enum SessionFlag { + Guest = 1 << 0, + Null = 1 << 1, + Encrypt = 1 << 2 +} + +export default SessionFlag; \ No newline at end of file diff --git a/src/protocol/smb2/ShareAccessType.ts b/src/protocols/smb2/ShareAccessType.ts similarity index 100% rename from src/protocol/smb2/ShareAccessType.ts rename to src/protocols/smb2/ShareAccessType.ts diff --git a/src/protocol/smb2/StatusCode.ts b/src/protocols/smb2/StatusCode.ts similarity index 88% rename from src/protocol/smb2/StatusCode.ts rename to src/protocols/smb2/StatusCode.ts index e27d728..b3b3ff6 100644 --- a/src/protocol/smb2/StatusCode.ts +++ b/src/protocols/smb2/StatusCode.ts @@ -4,6 +4,7 @@ enum StatusCode { MoreProcessingRequired = 0xc0000016, FileNameNotFound = 0xc0000034, FilePathNotFound = 0xc000003a, + LogonFailure = 0xC000006D, FileClosed = 0xc0000128 } diff --git a/src/protocols/smb2/TreeConnectShareCapability.ts b/src/protocols/smb2/TreeConnectShareCapability.ts new file mode 100644 index 0000000..25042e5 --- /dev/null +++ b/src/protocols/smb2/TreeConnectShareCapability.ts @@ -0,0 +1,8 @@ +enum TreeConnectShareCapability { + DistributedFileSystem = 1 << 3, + ContinuousAvailability = 1 << 4, + Scaleout = 1 << 5, + Cluster = 1 << 6 +} + +export default TreeConnectShareCapability; \ No newline at end of file diff --git a/src/protocols/smb2/TreeConnectShareFlag.ts b/src/protocols/smb2/TreeConnectShareFlag.ts new file mode 100644 index 0000000..1f6991d --- /dev/null +++ b/src/protocols/smb2/TreeConnectShareFlag.ts @@ -0,0 +1,18 @@ +enum TreeConnectShareFlag { + ManualCaching = 0x00000000, + DistributedFileSystem = 0x00000001, + DistributedFileSystemRoot = 0x00000002, + AutoCaching = 0x00000010, + VdoCaching = 0x00000020, + NoCaching = 0x00000030, + RestrictExclusiveOpens = 0x00000100, + ForceSharedDelete = 0x00000200, + AllowNamespaceCaching = 0x00000400, + AccessBasedDirectoryEnum = 0x00000800, + ForceLevel2Oplock = 0x00001000, + EnableHashV1 = 0x00002000, + EnableHashV2 = 0x00004000, + EncryptData = 0x00008000 +} + +export default TreeConnectShareFlag; \ No newline at end of file diff --git a/src/protocols/smb2/TreeConnectShareType.ts b/src/protocols/smb2/TreeConnectShareType.ts new file mode 100644 index 0000000..5903940 --- /dev/null +++ b/src/protocols/smb2/TreeConnectShareType.ts @@ -0,0 +1,7 @@ +enum TreeConnectShareType { + Disk = 0x01, + Pipe = 0x02, + Print = 0x03 +} + +export default TreeConnectShareType; \ No newline at end of file diff --git a/src/protocol/smb2/packets/ChangeNotify.ts b/src/protocols/smb2/packets/ChangeNotify.ts similarity index 97% rename from src/protocol/smb2/packets/ChangeNotify.ts rename to src/protocols/smb2/packets/ChangeNotify.ts index 4072b21..1ca611d 100644 --- a/src/protocol/smb2/packets/ChangeNotify.ts +++ b/src/protocols/smb2/packets/ChangeNotify.ts @@ -1,7 +1,7 @@ import * as util from "../../util"; import Structure from "../../Structure"; import FileAction from "../FileAction"; -import ChangeEntry from "../../models/ChangeEntry"; +import ChangeEntry from "../../../models/ChangeEntry"; import * as structureUtil from "../../structureUtil"; export enum Flags { diff --git a/src/protocol/smb2/packets/Close.ts b/src/protocols/smb2/packets/Close.ts similarity index 100% rename from src/protocol/smb2/packets/Close.ts rename to src/protocols/smb2/packets/Close.ts diff --git a/src/protocol/smb2/packets/Create.ts b/src/protocols/smb2/packets/Create.ts similarity index 100% rename from src/protocol/smb2/packets/Create.ts rename to src/protocols/smb2/packets/Create.ts diff --git a/src/protocol/smb2/packets/Echo.ts b/src/protocols/smb2/packets/Echo.ts similarity index 100% rename from src/protocol/smb2/packets/Echo.ts rename to src/protocols/smb2/packets/Echo.ts diff --git a/src/protocol/smb2/packets/Flush.ts b/src/protocols/smb2/packets/Flush.ts similarity index 100% rename from src/protocol/smb2/packets/Flush.ts rename to src/protocols/smb2/packets/Flush.ts diff --git a/src/protocols/smb2/packets/InputOutputControl.ts b/src/protocols/smb2/packets/InputOutputControl.ts new file mode 100644 index 0000000..cb7d0e6 --- /dev/null +++ b/src/protocols/smb2/packets/InputOutputControl.ts @@ -0,0 +1,124 @@ +import Structure from "../../Structure"; + +const requestStructure: Structure = { + structureSize: { + type: Number, + size: 2, + defaultValue: 57 + }, + reserved: { + type: Number, + size: 2 + }, + controlCode: { + type: Number, + size: 4 + }, + fileId: { + type: String, + encoding: "hex", + size: 16 + }, + inputOffset: { + type: Number, + size: 4 + }, + inputCount: { + type: Number, + size: 4 + }, + maxInputResponse: { + type: Number, + size: 4 + }, + outputOffset: { + type: Number, + size: 4 + }, + outputCount: { + type: Number, + size: 4 + }, + maxOutputResponse: { + type: Number, + size: 4 + }, + flags: { + type: Number, + size: 4 + }, + reserved2: { + type: Number, + size: 4 + }, + input: { + type: Buffer, + offsetFieldName: "inputOffset", + sizeFieldName: "inputCount" + }, + output: { + type: Buffer, + offsetFieldName: "outputOffset", + sizeFieldName: "outputCount" + } +}; + +const responseStructure: Structure = { + structureSize: { + type: Number, + size: 2, + defaultValue: 49 + }, + reserved: { + type: Number, + size: 2 + }, + controlCode: { + type: Number, + size: 4 + }, + fileId: { + type: String, + encoding: "hex", + size: 16 + }, + inputOffset: { + type: Number, + size: 4 + }, + inputCount: { + type: Number, + size: 4 + }, + outputOffset: { + type: Number, + size: 4 + }, + outputCount: { + type: Number, + size: 4 + }, + flags: { + type: Number, + size: 4 + }, + reserved2: { + type: Number, + size: 4 + }, + input: { + type: Buffer, + offsetFieldName: "inputOffset", + sizeFieldName: "inputCount" + }, + output: { + type: Buffer, + offsetFieldName: "outputOffset", + sizeFieldName: "outputCount" + } +}; + +export default { + requestStructure, + responseStructure +}; \ No newline at end of file diff --git a/src/protocol/smb2/packets/Negotiate.ts b/src/protocols/smb2/packets/Negotiate.ts similarity index 98% rename from src/protocol/smb2/packets/Negotiate.ts rename to src/protocols/smb2/packets/Negotiate.ts index 448dfaf..936d08c 100644 --- a/src/protocol/smb2/packets/Negotiate.ts +++ b/src/protocols/smb2/packets/Negotiate.ts @@ -74,7 +74,7 @@ const responseStructure: Structure = { type: Number, size: 4 }, - maxTransactSize: { + maxTransactionSize: { type: Number, size: 4 }, diff --git a/src/protocol/smb2/packets/QueryDirectory.ts b/src/protocols/smb2/packets/QueryDirectory.ts similarity index 87% rename from src/protocol/smb2/packets/QueryDirectory.ts rename to src/protocols/smb2/packets/QueryDirectory.ts index 51ef91b..024a743 100644 --- a/src/protocol/smb2/packets/QueryDirectory.ts +++ b/src/protocols/smb2/packets/QueryDirectory.ts @@ -1,8 +1,9 @@ +import dtyp from "dtyp"; import * as util from "../../util"; import Structure from "../../Structure"; import FileAttribute from "../FileAttribute"; import * as structureUtil from "../../structureUtil"; -import DirectoryEntry from "../../models/DirectoryEntry"; +import DirectoryEntry from "../../../models/DirectoryEntry"; const requestStructure: Structure = { structureSize: { @@ -72,16 +73,16 @@ const parseDirectoryEntry = (entryBuffer: Buffer): DirectoryEntry => { const index = entryBuffer.readUInt32LE(offset); offset += 4; - const creationTime = structureUtil.parseDate(entryBuffer.slice(offset, offset + 8)); + const creationTime = dtyp.parseFiletime(entryBuffer.slice(offset, offset + 8)); offset += 8; - const lastAccessTime = structureUtil.parseDate(entryBuffer.slice(offset, offset + 8)); + const lastAccessTime = dtyp.parseFiletime(entryBuffer.slice(offset, offset + 8)); offset += 8; - const lastWriteTime = structureUtil.parseDate(entryBuffer.slice(offset, offset + 8)); + const lastWriteTime = dtyp.parseFiletime(entryBuffer.slice(offset, offset + 8)); offset += 8; - const changeTime = structureUtil.parseDate(entryBuffer.slice(offset, offset + 8)); + const changeTime = dtyp.parseFiletime(entryBuffer.slice(offset, offset + 8)); offset += 8; const fileSize = entryBuffer.readBigUInt64LE(offset); diff --git a/src/protocol/smb2/packets/Read.ts b/src/protocols/smb2/packets/Read.ts similarity index 100% rename from src/protocol/smb2/packets/Read.ts rename to src/protocols/smb2/packets/Read.ts diff --git a/src/protocol/smb2/packets/LogOff.ts b/src/protocols/smb2/packets/SessionLogoff.ts similarity index 100% rename from src/protocol/smb2/packets/LogOff.ts rename to src/protocols/smb2/packets/SessionLogoff.ts diff --git a/src/protocol/smb2/packets/SessionSetup.ts b/src/protocols/smb2/packets/SessionSetup.ts similarity index 100% rename from src/protocol/smb2/packets/SessionSetup.ts rename to src/protocols/smb2/packets/SessionSetup.ts diff --git a/src/protocol/smb2/packets/SetInfo.ts b/src/protocols/smb2/packets/SetInfo.ts similarity index 100% rename from src/protocol/smb2/packets/SetInfo.ts rename to src/protocols/smb2/packets/SetInfo.ts diff --git a/src/protocol/smb2/packets/TreeConnect.ts b/src/protocols/smb2/packets/TreeConnect.ts similarity index 83% rename from src/protocol/smb2/packets/TreeConnect.ts rename to src/protocols/smb2/packets/TreeConnect.ts index 92a5d27..af7f759 100644 --- a/src/protocol/smb2/packets/TreeConnect.ts +++ b/src/protocols/smb2/packets/TreeConnect.ts @@ -1,4 +1,5 @@ import Structure from "../../Structure"; +import TreeConnectShareType from "../TreeConnectShareType"; const requestStructure: Structure = { structureSize: { @@ -28,11 +29,13 @@ const requestStructure: Structure = { const responseStructure: Structure = { structureSize: { type: Number, - size: 2 + size: 2, + defaultValue: 16 }, shareType: { type: Number, - size: 1 + size: 1, + defaultValue: TreeConnectShareType.Disk }, reserved: { type: Number, diff --git a/src/protocol/smb2/packets/TreeDisconnect.ts b/src/protocols/smb2/packets/TreeDisconnect.ts similarity index 100% rename from src/protocol/smb2/packets/TreeDisconnect.ts rename to src/protocols/smb2/packets/TreeDisconnect.ts diff --git a/src/protocol/smb2/packets/Write.ts b/src/protocols/smb2/packets/Write.ts similarity index 100% rename from src/protocol/smb2/packets/Write.ts rename to src/protocols/smb2/packets/Write.ts diff --git a/src/protocol/smb2/packets/index.ts b/src/protocols/smb2/packets/index.ts similarity index 77% rename from src/protocol/smb2/packets/index.ts rename to src/protocols/smb2/packets/index.ts index 7a66967..9697abe 100644 --- a/src/protocol/smb2/packets/index.ts +++ b/src/protocols/smb2/packets/index.ts @@ -1,6 +1,6 @@ export { default as Negotiate } from "./Negotiate"; export { default as SessionSetup } from "./SessionSetup"; -export { default as LogOff } from "./LogOff"; +export { default as SessionLogoff } from "./SessionLogoff"; export { default as TreeConnect } from "./TreeConnect"; export { default as TreeDisconnect } from "./TreeDisconnect"; export { default as Create } from "./Create"; @@ -11,4 +11,5 @@ export { default as Write } from "./Write"; export { default as Echo } from "./Echo"; export { default as QueryDirectory } from "./QueryDirectory"; export { default as ChangeNotify } from "./ChangeNotify"; -export { default as SetInfo } from "./SetInfo"; \ No newline at end of file +export { default as SetInfo } from "./SetInfo"; +export { default as InputOutputControl } from "./InputOutputControl"; \ No newline at end of file diff --git a/src/protocol/structureUtil.ts b/src/protocols/structureUtil.ts similarity index 79% rename from src/protocol/structureUtil.ts rename to src/protocols/structureUtil.ts index 38bf087..360af7f 100644 --- a/src/protocol/structureUtil.ts +++ b/src/protocols/structureUtil.ts @@ -1,9 +1,8 @@ import Value from "./Value"; import Structure from "./Structure"; -import moment from "moment-timezone"; import StructureField from "./StructureField"; -export const parseStructure = (buffer: Buffer, structure: Structure) => { +export const parseStructure = (buffer: Buffer, structure: Structure, options: { subtractOffset?: number; } = {}) => { let offset = 0; const data: any = {}; const structureFieldNames = Object.keys(structure); @@ -26,7 +25,15 @@ export const parseStructure = (buffer: Buffer, structure: Structure) => { if (typeof structureField.count === "undefined") throw new Error(`invalid_count_field_name: ${structureField.countFieldName}`); } - const value = buffer.slice(offset, offset + (size * structureField.count)); + let currentOffset = offset; + if (structureField.offsetFieldName) { + currentOffset = data[structureField.offsetFieldName] as number; + if (typeof currentOffset === "undefined") throw new Error(`invalid_offset_field_name: ${structureField.offsetFieldName}`); + + if (typeof options.subtractOffset === "number") currentOffset -= options.subtractOffset; + } + + const value = buffer.slice(currentOffset, currentOffset + (size * structureField.count)); data[structureFieldName] = parseValue(value, structureField); offset += size * structureField.count; } @@ -78,11 +85,6 @@ export const parseString = (buffer: Buffer, structureField: StructureField) => { return buffer.slice(0, structureField.size).toString(structureField.encoding); }; -export const parseDate = (buffer: Buffer) => { - const milliseconds = Number(buffer.readBigUInt64LE(0) / 10000n); - return moment.utc("1601-01-01").add(milliseconds, "milliseconds").toDate(); -}; - export const parseEnumValue = (enumObject: any, value: number | string) => { return Object.keys(enumObject) .find(x => enumObject[x] === value); @@ -117,8 +119,16 @@ export const parseList = (buffer: Buffer, parser: (entryBuffer: return list; }; -export const serializeStructure = (structure: Structure, data: any) => { - const normalizedData: { [fieldName: string]: { value?: Buffer; size?: number; } } = {}; +export const serializeStructure = (structure: Structure, data: any, options: { addOffset?: number; } = {}) => { + const normalizedData: { + [fieldName: string]: { + value?: Buffer; + size?: number; + offset?: number; + structureFieldName?: string; + structureField?: StructureField; + } + } = {}; const structureFieldNames = Object.keys(structure); for (const structureFieldName of structureFieldNames) { const structureField = structure[structureFieldName]; @@ -160,17 +170,40 @@ export const serializeStructure = (structure: Structure, data: any) => { normalizedData[structureFieldName].size = structureField.size * structureField.count; } - const normalizedFields = structureFieldNames.map(x => normalizedData[x]); + const normalizedFields = structureFieldNames.map(structureFieldName => { + const structureField = structure[structureFieldName]; + const normalizedField = normalizedData[structureFieldName]; + normalizedField.structureFieldName = structureFieldName; + normalizedField.structureField = structureField; + + return normalizedField; + }); const bufferSize = normalizedFields.reduce((prev, current) => prev + current.size, 0); const buffer = Buffer.allocUnsafe(bufferSize); let offset = 0; for (const normalizedField of normalizedFields) { - normalizedField.value.copy(buffer, offset); + if ( + normalizedField.structureField && + normalizedField.structureField.offsetFieldName + ) { + let currentOffset = offset; + if (typeof options.addOffset === "number") { + currentOffset += options.addOffset; + } + + const offsetField = normalizedData[normalizedField.structureField.offsetFieldName]; + offsetField.value = serializeValue(currentOffset, offsetField.structureField); + } + normalizedField.offset = offset; offset += normalizedField.size; } + for (const normalizedField of normalizedFields) { + normalizedField.value.copy(buffer, normalizedField.offset); + } + return buffer; }; @@ -193,13 +226,6 @@ export const serializeValue = (value: Value, structureField: StructureField): Bu return result; }; -export const serializeDate = (date: Date) => { - const milliseconds = moment(date).diff(moment.utc("1601-01-01"), "milliseconds"); - const buffer = Buffer.allocUnsafe(8); - buffer.writeBigInt64LE(BigInt(milliseconds) * 10000n, 0); - return buffer; -}; - export const serializeString = (value: string, structureField: StructureField) => { return Buffer.from(value, structureField.encoding); }; \ No newline at end of file diff --git a/src/protocol/util.ts b/src/protocols/util.ts similarity index 68% rename from src/protocol/util.ts rename to src/protocols/util.ts index 3ac75d6..4b1d510 100644 --- a/src/protocol/util.ts +++ b/src/protocols/util.ts @@ -53,4 +53,32 @@ export const generateGuid = () => { node.copy(buffer, offset); return buffer; +}; + +export const generateUint = (bits = 32) => Math.floor(Math.random() * Math.pow(2, bits)); + +export const formatBuffer = (buffer: Buffer) => + Array.from(buffer) + .map(byte => { + const text = byte.toString(16); + return text.length === 1 ? "0" + text : text; + }) + .join(" "); + +export const stringify = (object: any) => { + const formattedObject: any = {}; + const keys = Object.keys(object); + + for (const key of keys) { + const value = object[key]; + formattedObject[key] = stringifyValue(value); + } + + return JSON.stringify(formattedObject, null, 2); +}; + +export const stringifyValue = (value: any) => { + if (Buffer.isBuffer(value)) return formatBuffer(value); + if (typeof value === "bigint") return value.toString(); + return value; }; \ No newline at end of file diff --git a/src/server/AuthenticationProvider.ts b/src/server/AuthenticationProvider.ts new file mode 100644 index 0000000..d2c7f0a --- /dev/null +++ b/src/server/AuthenticationProvider.ts @@ -0,0 +1,20 @@ +import User from "./User"; + +export default class AuthenticationProvider { + public domain: string; + public users: User[] = []; + + async init() { } + + matchDomain(domain: string) { + if (!this.domain) return true; + return this.domain === domain; + } + + getUser(domain: string, username: string) { + if (!this.matchDomain(domain)) return null; + + const user = this.users.find(x => x.name === username); + return user || null; + } +} \ No newline at end of file diff --git a/src/server/Client.ts b/src/server/Client.ts index 6c31d56..fc0d939 100644 --- a/src/server/Client.ts +++ b/src/server/Client.ts @@ -3,22 +3,26 @@ import Server from "./Server"; import { EventEmitter } from "events"; import SmbRequest from "./SmbRequest"; import Smb2Request from "./Smb2Request"; -import Packet from "../protocol/Packet"; -import Request from "../protocol/Request"; -import Response from "../protocol/Response"; -import Dialect from "../protocol/smb2/Dialect"; -import * as protocolIds from "../protocol/protocolIds"; +import Packet from "../protocols/Packet"; +import Request from "../protocols/Request"; +import Response from "../protocols/Response"; +import * as ProtocolIds from "../protocols/ProtocolIds"; +import Dialect, { formatDialectName } from "../protocols/smb2/Dialect"; interface Client { on(event: "request", callback: (req: Request) => void): this; + on(event: "destroy", callback: () => void): this; once(event: "request", callback: (req: Request) => void): this; + once(event: "destroy", callback: () => void): this; } class Client extends EventEmitter { private restChunk: Buffer; public targetDialect: Dialect; public targetDialectName: string; + public serverChallenge: Buffer; + public useExtendedSessionSecurity: boolean = false; constructor( private server: Server, @@ -27,10 +31,17 @@ class Client extends EventEmitter { super(); } - setup() { + init() { this.socket.setNoDelay(true); this.socket.addListener("data", this.onData); + this.socket.addListener("error", this.onError); + this.socket.addListener("close", this.onClose); + } + + setTargetDialect(dialect: Dialect) { + this.targetDialect = dialect; + this.targetDialectName = formatDialectName(dialect); } private onData = (buffer: Buffer) => { @@ -47,7 +58,7 @@ class Client extends EventEmitter { for (const chunk of chunks) { const protocolId = Packet.parseProtocolId(chunk); - if (protocolId === protocolIds.smb) { + if (protocolId === ProtocolIds.Smb) { const request = SmbRequest.parse(chunk); this.emit("request", request); } else { @@ -57,6 +68,22 @@ class Client extends EventEmitter { } } + private onError = (err: Error) => { + this.destroy(); + } + + private onClose = () => { + this.destroy(); + } + + destroy() { + this.socket.destroy(); + this.socket.removeAllListeners(); + delete this.socket; + + this.emit("destroy"); + } + send(response: Response) { const buffer = response.serialize(); this.socket.write(buffer); diff --git a/src/server/RemoteService.ts b/src/server/RemoteService.ts new file mode 100644 index 0000000..2255acf --- /dev/null +++ b/src/server/RemoteService.ts @@ -0,0 +1,3 @@ +export default abstract class RemoteService { + public abstract serialize(): Buffer; +} \ No newline at end of file diff --git a/src/server/Server.ts b/src/server/Server.ts index 3507b48..9c93b96 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -5,16 +5,20 @@ import Response from "./Response"; import moment from "moment-timezone"; import Middleware from "./Middleware"; import SmbResponse from "./SmbResponse"; -import * as util from "../protocol/util"; +import * as util from "../protocols/util"; import Smb2Response from "./Smb2Response"; -import SmbHeader from "../protocol/smb/Header"; -import Smb2Header from "../protocol/smb2/Header"; +import ShareProvider from "./ShareProvider"; +import SmbHeader from "../protocols/smb/Header"; +import Smb2Header from "../protocols/smb2/Header"; import requestType from "./middlewares/requestType"; -import * as protocolIds from "../protocol/protocolIds"; -import SmbPacketType from "../protocol/smb/PacketType"; -import Smb2PacketType from "../protocol/smb2/PacketType"; +import Smb2Dialect from "../protocols/smb2/Dialect"; +import * as ProtocolIds from "../protocols/ProtocolIds"; +import SmbPacketType from "../protocols/smb/PacketType"; +import Smb2PacketType from "../protocols/smb2/PacketType"; import * as smbRequestHandlers from "./requestHandlers/smb"; import * as smb2RequestHandlers from "./requestHandlers/smb2"; +import unhandledRequest from "./middlewares/unhandledRequest"; +import AuthenticationProvider from "./AuthenticationProvider"; import supportedProtocols from "./middlewares/supportedProtocols"; export default class Server { @@ -24,14 +28,19 @@ export default class Server { public startDate: Date; public guid = util.generateGuid(); private middlewares: Middleware[] = []; - - constructor() { - this.use(supportedProtocols([protocolIds.smb, protocolIds.smb2])); + private authenticationProviders: AuthenticationProvider[] = []; + private shareProviders: ShareProvider[] = []; + private nextSessionId: bigint = 0n; + public supportedSmb2Dialects = [ + Smb2Dialect.Smb202 + ]; + async init() { + this.use(supportedProtocols([ProtocolIds.Smb, ProtocolIds.Smb2])); const smb2RequestHandlerTypes = Object.keys(smb2RequestHandlers); for (const smb2RequestHandlerType of smb2RequestHandlerTypes) { const handler = requestType( - protocolIds.smb2, + ProtocolIds.Smb2, Smb2PacketType[smb2RequestHandlerType], smb2RequestHandlers[smb2RequestHandlerType] ); @@ -41,20 +50,30 @@ export default class Server { const smbRequestHandlerTypes = Object.keys(smbRequestHandlers); for (const smbRequestHandlerType of smbRequestHandlerTypes) { const handler = requestType( - protocolIds.smb, + ProtocolIds.Smb, SmbPacketType[smbRequestHandlerType], smbRequestHandlers[smbRequestHandlerType] ); this.use(handler); } + this.use(unhandledRequest); + + for (const authenticationProvider of this.authenticationProviders) { + await authenticationProvider.init(); + } + + for (const shareProvider of this.shareProviders) { + await shareProvider.init(); + } + this.server.addListener("connection", this.onConnection); } async listen(port: number = 445) { this.port = port; - await new Promise((resolve) => { + await new Promise(resolve => { this.server.listen({ port }, () => { resolve(); }); @@ -68,7 +87,11 @@ export default class Server { private onConnection = (socket: Socket) => { const client = new Client(this, socket); client.on("request", this.onRequest(client)); - client.setup(); + client.once("destroy", () => { + const index = this.clients.indexOf(client); + if (index !== -1) this.clients.splice(index, 1); + }); + client.init(); this.clients.push(client); } @@ -85,7 +108,7 @@ export default class Server { async handleRequest(req: Request) { let res: Response; - if (req.header.protocolId === protocolIds.smb) { + if (req.header.protocolId === ProtocolIds.Smb) { const header = req.header as SmbHeader; res = new SmbResponse({ protocolId: header.protocolId, @@ -101,17 +124,20 @@ export default class Server { messageId: header.messageId, clientId: header.clientId, treeId: header.treeId, - sessionId: header.sessionId, - signature: header.signature + sessionId: header.sessionId }); } req.response = res; - for (const middleware of this.middlewares) { - await middleware(req, res); - if (res.sent) return req.client.send(res); - if (res.redirectedReq) return await this.redirect(req, res.redirectedReq); + try { + for (const middleware of this.middlewares) { + await middleware(req, res); + if (res.sent) return req.client.send(res); + if (res.redirectedRequest) return await this.redirect(req, res.redirectedRequest); + } + } catch (err) { + console.error(err); } } @@ -120,7 +146,29 @@ export default class Server { await this.handleRequest(to); } - use(middleware: Middleware) { - this.middlewares.push(middleware); + use(element: Middleware | AuthenticationProvider | ShareProvider) { + if (element instanceof AuthenticationProvider) { + this.authenticationProviders.push(element); + } else if (element instanceof ShareProvider) { + this.shareProviders.push(element); + } else { + this.middlewares.push(element); + } + } + + findUser(domain: string, username: string) { + for (const authenticationProvider of this.authenticationProviders) { + const user = authenticationProvider.getUser(domain, username); + if (user) return user; + } + return null; + } + + findShare(name: string) { + for (const shareProvider of this.shareProviders) { + const share = shareProvider.getShare(name); + if (share) return share; + } + return null; } } \ No newline at end of file diff --git a/src/server/Share.ts b/src/server/Share.ts new file mode 100644 index 0000000..dd31927 --- /dev/null +++ b/src/server/Share.ts @@ -0,0 +1,3 @@ +export default class Share { + name: string; +} \ No newline at end of file diff --git a/src/server/ShareProvider.ts b/src/server/ShareProvider.ts new file mode 100644 index 0000000..f98581b --- /dev/null +++ b/src/server/ShareProvider.ts @@ -0,0 +1,12 @@ +import Share from "./Share"; + +export default abstract class ShareProvider { + public shares: Share[] = []; + + async init() { } + + getShare(name: string) { + const share = this.shares.find(x => x.name === name); + return share || null; + } +} \ No newline at end of file diff --git a/src/server/Smb2Request.ts b/src/server/Smb2Request.ts index 3370b09..b9b0a50 100644 --- a/src/server/Smb2Request.ts +++ b/src/server/Smb2Request.ts @@ -1,6 +1,6 @@ import Client from "./Client"; import Server from "./Server"; -import ProtocolSmb2Request from "../protocol/smb2/Request"; +import ProtocolSmb2Request from "../protocols/smb2/Request"; export default class Smb2Request extends ProtocolSmb2Request { server?: Server; diff --git a/src/server/Smb2Response.ts b/src/server/Smb2Response.ts index e08c056..8b068d5 100644 --- a/src/server/Smb2Response.ts +++ b/src/server/Smb2Response.ts @@ -1,10 +1,10 @@ import Request from "./Request"; -import Value from "../protocol/Value"; -import ProtocolSmb2Response from "../protocol/smb2/Response"; +import Value from "../protocols/Value"; +import ProtocolSmb2Response from "../protocols/smb2/Response"; export default class Smb2Response extends ProtocolSmb2Response { sent: boolean = false; - redirectedReq: Request; + redirectedRequest: Request; public status(status: number) { this.header.status = status; @@ -16,12 +16,15 @@ export default class Smb2Response extends ProtocolSmb2Response { this.header[name] = value; } - public send(data: any) { - this.body = data; + public send(data: any = {}) { + this.body = { + ...(this.body || {}), + ...data + }; this.sent = true; } public redirect(req: Request) { - this.redirectedReq = req; + this.redirectedRequest = req; } } \ No newline at end of file diff --git a/src/server/SmbRequest.ts b/src/server/SmbRequest.ts index a2c7ad7..1033628 100644 --- a/src/server/SmbRequest.ts +++ b/src/server/SmbRequest.ts @@ -1,6 +1,6 @@ import Client from "./Client"; import Server from "./Server"; -import ProtocolSmbRequest from "../protocol/smb/Request"; +import ProtocolSmbRequest from "../protocols/smb/Request"; export default class SmbRequest extends ProtocolSmbRequest { server?: Server; diff --git a/src/server/SmbResponse.ts b/src/server/SmbResponse.ts index c41a154..ccdfcc8 100644 --- a/src/server/SmbResponse.ts +++ b/src/server/SmbResponse.ts @@ -1,10 +1,10 @@ import Request from "./Request"; -import Value from "../protocol/Value"; -import ProtocolSmbResponse from "../protocol/smb/Response"; +import Value from "../protocols/Value"; +import ProtocolSmbResponse from "../protocols/smb/Response"; export default class SmbResponse extends ProtocolSmbResponse { sent: boolean = false; - redirectedReq: Request; + redirectedRequest: Request; public status(status: number) { this.header.status = status; @@ -22,6 +22,6 @@ export default class SmbResponse extends ProtocolSmbResponse { } public redirect(req: Request) { - this.redirectedReq = req; + this.redirectedRequest = req; } } \ No newline at end of file diff --git a/src/server/User.ts b/src/server/User.ts new file mode 100644 index 0000000..4d61474 --- /dev/null +++ b/src/server/User.ts @@ -0,0 +1,4 @@ +export default interface User { + name: string; + password: string; +} \ No newline at end of file diff --git a/src/server/authenticationProviders/FileAuthenticationProvider.ts b/src/server/authenticationProviders/FileAuthenticationProvider.ts new file mode 100644 index 0000000..394a0bc --- /dev/null +++ b/src/server/authenticationProviders/FileAuthenticationProvider.ts @@ -0,0 +1,14 @@ +import fs from "fs"; +import AuthenticationProvider from "../AuthenticationProvider"; + +export default class FileAuthenticationProvider extends AuthenticationProvider { + constructor( + private filename: string + ) { + super(); + } + + async init() { + this.users = JSON.parse(fs.readFileSync(this.filename, { encoding: "utf-8" })); + } +} \ No newline at end of file diff --git a/src/server/authenticationProviders/index.ts b/src/server/authenticationProviders/index.ts new file mode 100644 index 0000000..5f4d04c --- /dev/null +++ b/src/server/authenticationProviders/index.ts @@ -0,0 +1 @@ +export { default as FileAuthenticationProvider } from "./FileAuthenticationProvider"; \ No newline at end of file diff --git a/src/server/middlewares/requestType.ts b/src/server/middlewares/requestType.ts index bcb2c45..336cb6a 100644 --- a/src/server/middlewares/requestType.ts +++ b/src/server/middlewares/requestType.ts @@ -1,7 +1,7 @@ import Request from "../Smb2Request"; import Response from "../Smb2Response"; import Middleware from "../Middleware"; -import PacketType from "../../protocol/smb2/PacketType"; +import PacketType from "../../protocols/smb2/PacketType"; export default (protocolId: string, packetType: PacketType, middleware: Middleware) => async (req: Request, res: Response) => { if ( diff --git a/src/server/middlewares/unhandledRequest.ts b/src/server/middlewares/unhandledRequest.ts new file mode 100644 index 0000000..87ee02d --- /dev/null +++ b/src/server/middlewares/unhandledRequest.ts @@ -0,0 +1,7 @@ +import Request from "../Request"; +import Response from "../Response"; +import * as util from "../../protocols/util"; + +export default (req: Request, res: Response) => { + throw new Error(`unhandled request: ${util.stringify(req.header)} ${util.stringify(req.body)}`); +}; \ No newline at end of file diff --git a/src/server/remoteServices/WorkstationService.ts b/src/server/remoteServices/WorkstationService.ts new file mode 100644 index 0000000..045521b --- /dev/null +++ b/src/server/remoteServices/WorkstationService.ts @@ -0,0 +1,7 @@ +import RemoteService from "../RemoteService"; + +export default class WorkstationService extends RemoteService { + public serialize() { + return Buffer.allocUnsafe(0); + } +} \ No newline at end of file diff --git a/src/server/requestHandlers/smb/Negotiate.ts b/src/server/requestHandlers/smb/Negotiate.ts index 0722daa..2dda736 100644 --- a/src/server/requestHandlers/smb/Negotiate.ts +++ b/src/server/requestHandlers/smb/Negotiate.ts @@ -1,9 +1,9 @@ import Request from "../../SmbRequest"; import Response from "../../SmbResponse"; import Smb2Request from "../../Smb2Request"; -import Dialect from "../../../protocol/smb2/Dialect"; -import * as protocolIds from "../../../protocol/protocolIds"; -import Smb2PacketType from "../../../protocol/smb2/PacketType"; +import Dialect from "../../../protocols/smb2/Dialect"; +import * as ProtocolIds from "../../../protocols/ProtocolIds"; +import Smb2PacketType from "../../../protocols/smb2/PacketType"; const supportedDialects = [ "NT LM 0.12", @@ -20,7 +20,7 @@ export default (req: Request, res: Response) => { if (matchingDialects.find(x => x.startsWith("SMB 2."))) { const newReq = new Smb2Request({ - protocolId: protocolIds.smb2, + protocolId: ProtocolIds.Smb2, type: Smb2PacketType.Negotiate }, { dialects: [ diff --git a/src/server/requestHandlers/smb2/InputOutputControl.ts b/src/server/requestHandlers/smb2/InputOutputControl.ts new file mode 100644 index 0000000..2aed015 --- /dev/null +++ b/src/server/requestHandlers/smb2/InputOutputControl.ts @@ -0,0 +1,80 @@ +import Request from "../../Smb2Request"; +import Response from "../../Smb2Response"; +import StatusCode from "../../../protocols/smb2/StatusCode"; +import Capability from "../../../protocols/smb2/Capability"; +import ControlCode from "../../../protocols/smb2/ControlCode"; +import SecurityMode from "../../../protocols/smb2/SecurityMode"; +import * as structureUtil from "../../../protocols/structureUtil"; + +export default (req: Request, res: Response) => { + const controlCode = req.body.controlCode as number; + + if (controlCode === ControlCode.ValidateNegotiateInfo) { + if (req.body.fileId !== "f".repeat(32)) throw new Error(`invalid_validate_negotiate_info_request`); + + const input = structureUtil.parseStructure(req.body.input, { + capabilities: { + type: Number, + size: 4 + }, + fileId: { + type: String, + encoding: "hex", + size: 16 + }, + securityMode: { + type: Number, + size: 2, + defaultValue: 1 + }, + dialectCount: { + type: Number, + signedness: "Unsigned", + size: 2 + }, + dialects: { + type: Number, + countFieldName: "dialectCount", + size: 2 + } + }); + + const output = structureUtil.serializeStructure({ + capabilities: { + type: Number, + size: 4 + }, + fileId: { + type: String, + encoding: "hex", + size: 16 + }, + securityMode: { + type: Number, + size: 2, + defaultValue: 1 + }, + dialect: { + type: Number, + signedness: "Unsigned", + size: 2 + } + }, { + capabilities: Capability.DistributedFileSystem, + fileId: req.server.guid, + securityMode: SecurityMode.SigningEnabled, + dialect: req.client.targetDialect + }); + + res.status(StatusCode.Success); + res.send({ + controlCode, + fileId: req.body.fileId, + outputCount: output.length, + output + }); + return; + } + + throw new Error(`not_yet_implemented`); +}; \ No newline at end of file diff --git a/src/server/requestHandlers/smb2/Negotiate.ts b/src/server/requestHandlers/smb2/Negotiate.ts index 333a58b..742c5a8 100644 --- a/src/server/requestHandlers/smb2/Negotiate.ts +++ b/src/server/requestHandlers/smb2/Negotiate.ts @@ -1,45 +1,50 @@ +import dtyp from "dtyp"; import moment from "moment-timezone"; import Request from "../../Smb2Request"; import Response from "../../Smb2Response"; -import StatusCode from "../../../protocol/smb2/StatusCode"; -import Capability from "../../../protocol/smb2/Capability"; -import Smb2Dialect from "../../../protocol/smb2/Dialect"; -import { headerSize } from "../../../protocol/smb2/Header"; -import * as structureUtil from "../../../protocol/structureUtil"; - -const supportedDialects = [ - Smb2Dialect.Smb210, - Smb2Dialect.Smb202, - Smb2Dialect.Smb2xx -]; +import Smb2Dialect from "../../../protocols/smb2/Dialect"; +import StatusCode from "../../../protocols/smb2/StatusCode"; +import Capability from "../../../protocols/smb2/Capability"; +import { headerSize } from "../../../protocols/smb2/Header"; +import SecurityMode from "../../../protocols/smb2/SecurityMode"; export default (req: Request, res: Response) => { const dialects = req.body.dialects as Smb2Dialect[]; - const targetDialect = supportedDialects.find(supportedDialect => - dialects.find(dialect => dialect === supportedDialect) + const targetDialect = req.server.supportedSmb2Dialects.find( + supportedDialect => dialects.find( + dialect => dialect === supportedDialect + ) ); - const targetDialectName = structureUtil.parseEnumValue(Smb2Dialect, targetDialect); + + req.client.setTargetDialect(targetDialect); + + let capabilities = Capability.DistributedFileSystem; + if ( + targetDialect !== Smb2Dialect.Smb202 + ) { + capabilities |= Capability.Leasing; + capabilities |= Capability.LargeMtu; + } const securityBuffer = Buffer.alloc(0); res.status(StatusCode.Success); - res.set("clientId", req.header.clientId); res.send({ structureSize: 0x0041, - securityMode: 0, + securityMode: SecurityMode.SigningEnabled, dialectRevision: targetDialect, - reserved: 0, // NegotiateContextCount + reserved: 0, serverGuid: req.server.guid, - capabilities: Capability.DistributedFileSystem | Capability.MultiCreditSupport, - maxTransactSize: 0x00100000, - maxReadSize: 0x00100000, - maxWriteSize: 0x00100000, - systemTime: structureUtil.serializeDate(moment().toDate()), - serverStartTime: structureUtil.serializeDate(req.server.startDate), + capabilities, + maxTransactionSize: 0x00800000, + maxReadSize: 0x00800000, + maxWriteSize: 0x00800000, + systemTime: dtyp.serializeFiletime(moment().toDate()), + serverStartTime: dtyp.serializeFiletime(req.server.startDate), securityBufferOffset: headerSize + 64, securityBufferLength: securityBuffer.length, - reserved2: 0, // NegotiateContextOffset + reserved2: 0, buffer: securityBuffer }); }; \ No newline at end of file diff --git a/src/server/requestHandlers/smb2/SessionLogoff.ts b/src/server/requestHandlers/smb2/SessionLogoff.ts new file mode 100644 index 0000000..a7ee1b8 --- /dev/null +++ b/src/server/requestHandlers/smb2/SessionLogoff.ts @@ -0,0 +1,9 @@ +import Request from "../../Smb2Request"; +import Response from "../../Smb2Response"; +import StatusCode from "../../../protocols/smb2/StatusCode"; + +export default (req: Request, res: Response) => { + // TODO: proper logoff + res.status(StatusCode.Success); + res.send(); +}; \ No newline at end of file diff --git a/src/server/requestHandlers/smb2/SessionSetup.ts b/src/server/requestHandlers/smb2/SessionSetup.ts index 8908c4a..ccd1c66 100644 --- a/src/server/requestHandlers/smb2/SessionSetup.ts +++ b/src/server/requestHandlers/smb2/SessionSetup.ts @@ -1,31 +1,132 @@ +import os from "os"; +import moment from "moment-timezone"; import Request from "../../Smb2Request"; import Response from "../../Smb2Response"; -import * as ntlm from "../../../protocol/ntlm/util"; -import StatusCode from "../../../protocol/smb2/StatusCode"; -import NegotiateFlag from "../../../protocol/ntlm/NegotiateFlag"; +import StatusCode from "../../../protocols/smb2/StatusCode"; +import SessionFlag from "../../../protocols/smb2/SessionFlag"; + +import ntlmv2, { + MessageType as NtlmMessageType, + NegotiateFlag, + AttributeValueId +} from "ntlmv2"; export default (req: Request, res: Response) => { - // console.log(ntlm); - // console.log(req.body); - // console.log(); - console.log("SessionSetup"); + const buffer = req.body.buffer as Buffer; + const messageType = ntlmv2.parseMessageType(buffer); + if (messageType === NtlmMessageType.Negotiation) { + handleNegotiationRequest(req, res); + } else if (messageType === NtlmMessageType.Authentication) { + handleAuthenticationRequest(req, res); + } +}; + +const handleNegotiationRequest = (req: Request, res: Response) => { + const buffer = req.body.buffer as Buffer; + const negotiationMessage = ntlmv2.parseNegotiationMessage(buffer); + + if ((negotiationMessage.negotiateFlags & NegotiateFlag.ExtendedSessionSecurity) > 0) { + req.client.useExtendedSessionSecurity = true; + } + + const ntlmChallengeNegotiationFlags = syncNegotiationFlags(negotiationMessage.negotiateFlags); - const decodedNtlmNegotiation = ntlm.decodeNegotiationMessage(req.body.buffer as Buffer); - // console.log(ntlmData); + const hostname = os.hostname(); + const targetInfo = [{ + id: AttributeValueId.NetBiosDomainName, + value: hostname + }, { + id: AttributeValueId.NetBiosComputerName, + value: hostname + }, { + id: AttributeValueId.DnsDomainName, + value: hostname + }, { + id: AttributeValueId.DnsComputerName, + value: hostname + }, { + id: AttributeValueId.Timestamp, + value: moment().toDate() + }]; - const ntlmChallengeNegotiationFlags = syncNegotiationFlags(decodedNtlmNegotiation.negotiateFlags); - const encodedNtlmChallenge = ntlm.encodeChallengeMessage(ntlmChallengeNegotiationFlags); + const serverChallenge = ntlmv2.generateServerChallenge(); + req.client.serverChallenge = serverChallenge; + const challengeMessage = ntlmv2.serializeChallengeMessage(hostname, targetInfo, ntlmChallengeNegotiationFlags, serverChallenge); - // console.log(encodedNtlmChallenge.toString("hex")); - res.status(StatusCode.MoreProcessingRequired); // first session setup request (second success) - res.set("clientId", req.header.clientId); + res.status(StatusCode.MoreProcessingRequired); res.send({ structureSize: 9, sessionFlags: 0, securityBufferOffset: 72, securityBufferLength: 178, - buffer: encodedNtlmChallenge + buffer: challengeMessage + }); +}; + +const handleAuthenticationRequest = (req: Request, res: Response) => { + const buffer = req.body.buffer as Buffer; + const authenticationMessage = ntlmv2.parseAuthenticationMessage(buffer); + + let authenticated = false; + const isRequestingAnonymous = (authenticationMessage.negotiateFlags & NegotiateFlag.Anonymous) > 0; + + if (isRequestingAnonymous) { + authenticated = true; + + res.status(StatusCode.Success); + sendEmptyBody(res, { + sessionFlags: SessionFlag.Guest + }); + return; + } else { + const user = req.server.findUser(authenticationMessage.domain, authenticationMessage.username); + if (!user) { + res.status(StatusCode.LogonFailure); + sendEmptyBody(res); + return; + } + + if (req.client.useExtendedSessionSecurity) { + if (ntlmv2.isExtendedSessionSecurityLmResponse(authenticationMessage.lmResponse)) { + throw new Error(`not_yet_implemented`); + } else { + authenticated = ntlmv2.matchExtendedSessionSecurityPasswordV2( + user.password, + req.client.serverChallenge, + authenticationMessage.lmResponse, + authenticationMessage.ntResponse, + authenticationMessage.domain, + authenticationMessage.username + ); + } + } else { + authenticated = ntlmv2.matchPassword( + user.password, + req.client.serverChallenge, + authenticationMessage.lmResponse, + authenticationMessage.ntResponse + ); + } + } + + if (authenticated) { + res.status(StatusCode.Success); + } else { + res.status(StatusCode.LogonFailure); + } + + sendEmptyBody(res); +}; + +const sendEmptyBody = (res: Response, overwrite: any = {}) => { + res.send({ + structureSize: 9, + sessionFlags: 0, + securityBufferOffset: 72, + securityBufferLength: 0, + buffer: Buffer.allocUnsafe(0), + ...overwrite }); }; @@ -40,15 +141,13 @@ const syncNegotiationFlags = (negotiationFlags: number) => { if ((negotiationFlags & NegotiateFlag.UnicodeEncoding) > 0) { challengeNegotiateFlags |= NegotiateFlag.UnicodeEncoding; - } - else if ((negotiationFlags & NegotiateFlag.OemEncoding) > 0) { + } else if ((negotiationFlags & NegotiateFlag.OemEncoding) > 0) { challengeNegotiateFlags |= NegotiateFlag.OemEncoding; } if ((negotiationFlags & NegotiateFlag.ExtendedSessionSecurity) > 0) { challengeNegotiateFlags |= NegotiateFlag.ExtendedSessionSecurity; - } - else if ((negotiationFlags & NegotiateFlag.LanManagerSessionKey) > 0) { + } else if ((negotiationFlags & NegotiateFlag.LanManagerSessionKey) > 0) { challengeNegotiateFlags |= NegotiateFlag.LanManagerSessionKey; } @@ -64,8 +163,10 @@ const syncNegotiationFlags = (negotiationFlags: number) => { challengeNegotiateFlags |= NegotiateFlag.Seal; } - if ((negotiationFlags & NegotiateFlag.Sign) > 0 || - (negotiationFlags & NegotiateFlag.Seal) > 0) { + if ( + (negotiationFlags & NegotiateFlag.Sign) > 0 || + (negotiationFlags & NegotiateFlag.Seal) > 0 + ) { if ((negotiationFlags & NegotiateFlag.Use56BitEncryption) > 0) { // [MS-NLMP] If the client sends NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN with // NTLMSSP_NEGOTIATE_56 to the server in the NEGOTIATE_MESSAGE, the server MUST return diff --git a/src/server/requestHandlers/smb2/TreeConnect.ts b/src/server/requestHandlers/smb2/TreeConnect.ts new file mode 100644 index 0000000..df50238 --- /dev/null +++ b/src/server/requestHandlers/smb2/TreeConnect.ts @@ -0,0 +1,58 @@ +import url from "url"; +import Request from "../../Smb2Request"; +import Response from "../../Smb2Response"; +import * as util from "../../../protocols/util"; +import StatusCode from "../../../protocols/smb2/StatusCode"; +import TreeConnectShareType from "../../../protocols/smb2/TreeConnectShareType"; +import TreeConnectShareFlag from "../../../protocols/smb2/TreeConnectShareFlag"; +import FilePipePrinterAccess from "../../../protocols/smb2/FilePipePrinterAccess"; +import TreeConnectShareCapability from "../../../protocols/smb2/TreeConnectShareCapability"; + +export default (req: Request, res: Response) => { + const buffer = req.body.buffer as Buffer; + const fullUrl = util.toUnixPath(buffer.toString("ucs2")); + const parsedUrl = url.parse("smb:" + fullUrl); + const pathname = parsedUrl.pathname; + + const treeId = util.generateUint(32); + + const cachingMode = TreeConnectShareFlag.ManualCaching; + + if (pathname === "/IPC$") { + res.status(StatusCode.Success); + res.set("treeId", treeId); + res.send({ + shareType: TreeConnectShareType.Pipe, + shareFlags: ( + cachingMode + ), + maximalAccess: ( + FilePipePrinterAccess.ReadData | + FilePipePrinterAccess.ReadEa | + FilePipePrinterAccess.Execute | + FilePipePrinterAccess.ReadAttributes | + FilePipePrinterAccess.Delete | + FilePipePrinterAccess.ReadControl | + FilePipePrinterAccess.WriteDiscretionaryAccessControl | + FilePipePrinterAccess.WriteOwner | + FilePipePrinterAccess.Synchronize + ) + }); + return; + } + + res.status(StatusCode.Success); + res.set("treeId", treeId); + res.send({ + shareType: TreeConnectShareType.Disk, + shareFlags: ( + cachingMode | + TreeConnectShareFlag.DistributedFileSystem | + TreeConnectShareFlag.DistributedFileSystemRoot + ), + capabilities: ( + TreeConnectShareCapability.DistributedFileSystem + ), + maximalAccess: 0x001f01ff + }); +}; \ No newline at end of file diff --git a/src/server/requestHandlers/smb2/index.ts b/src/server/requestHandlers/smb2/index.ts index f38e484..dbdf309 100644 --- a/src/server/requestHandlers/smb2/index.ts +++ b/src/server/requestHandlers/smb2/index.ts @@ -1,2 +1,5 @@ export { default as Negotiate } from "./Negotiate"; -export { default as SessionSetup } from "./SessionSetup"; \ No newline at end of file +export { default as SessionSetup } from "./SessionSetup"; +export { default as SessionLogoff } from "./SessionLogoff"; +export { default as TreeConnect } from "./TreeConnect"; +export { default as InputOutputControl } from "./InputOutputControl"; \ No newline at end of file diff --git a/src/server/shareProviders/FileShareProvider.ts b/src/server/shareProviders/FileShareProvider.ts new file mode 100644 index 0000000..0b7dce9 --- /dev/null +++ b/src/server/shareProviders/FileShareProvider.ts @@ -0,0 +1,14 @@ +import fs from "fs"; +import ShareProvider from "../ShareProvider"; + +export default class FileShareProvider extends ShareProvider { + constructor( + private filename: string + ) { + super(); + } + + async init() { + this.shares = JSON.parse(fs.readFileSync(this.filename, { encoding: "utf-8" })); + } +} \ No newline at end of file diff --git a/src/server/shareProviders/index.ts b/src/server/shareProviders/index.ts new file mode 100644 index 0000000..c0d64cd --- /dev/null +++ b/src/server/shareProviders/index.ts @@ -0,0 +1 @@ +export { default as FileShareProvider } from "./FileShareProvider"; \ No newline at end of file