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

1110 lines
36 KiB

  1. /*++
  2. MAIN.C
  3. main program for the ktPass program
  4. Copyright (C) 1998 Microsoft Corporation, all rights reserved.
  5. Created, Jun 18, 1998 by DavidCHR.
  6. --*/
  7. #include "master.h"
  8. #include <winldap.h>
  9. #include "keytab.h"
  10. #include "keytypes.h"
  11. #include "secprinc.h"
  12. #include <kerbcon.h>
  13. #include <lm.h>
  14. #include "options.h"
  15. #include "delegtools.h"
  16. #include "delegation.h"
  17. #include <rpc.h>
  18. #include <ntdsapi.h>
  19. #include <dsgetdc.h>
  20. #include <windns.h>
  21. LPSTR KvnoAttribute = "msDS-KeyVersionNumber";
  22. #define KVNO_DETECT_AT_DC ( (ULONG) -1 )
  23. PVOID
  24. MIDL_user_allocate( size_t size ) {
  25. return malloc( size );
  26. }
  27. VOID
  28. MIDL_user_free( PVOID pvFree ) {
  29. free( pvFree );
  30. }
  31. // this global is set by the command line options.
  32. K5_INT16 ktvno = 0x0502; // kerberos 5, keytab v.2
  33. PKTFILE
  34. NewKt() {
  35. PKTFILE ret;
  36. ret = (PKTFILE) malloc (sizeof(KTFILE));
  37. if (!ret) {
  38. return NULL;
  39. }
  40. memset(ret, 0L, sizeof(KTFILE));
  41. ret->Version = ktvno;
  42. return ret;
  43. }
  44. #define MAYBE 2
  45. USHORT
  46. PromptResponse = MAYBE;
  47. BOOL
  48. UserWantsToDoItAnyway( IN LPSTR fmt,
  49. ... ) {
  50. va_list va;
  51. CHAR Buffer[ 5 ] = { '\0' }; /* == %c\r\n\0 */
  52. INT Response;
  53. BOOL ret = FALSE;
  54. BOOL keepGoing = TRUE;
  55. ULONG i;
  56. do {
  57. va_start( va, fmt );
  58. fprintf( stderr, "\n" );
  59. vfprintf( stderr,
  60. fmt,
  61. va );
  62. fprintf( stderr, " [y/n]? " );
  63. if ( PromptResponse != MAYBE ) {
  64. fprintf( stderr,
  65. "auto: %hs\n",
  66. PromptResponse ? "YES" : "NO" );
  67. return PromptResponse;
  68. }
  69. if ( !fgets( Buffer,
  70. sizeof( Buffer ),
  71. stdin ) ) {
  72. fprintf( stderr,
  73. "EOF on stdin. Assuming you mean no.\n" );
  74. return FALSE;
  75. }
  76. for ( i = 0; i < sizeof( Buffer ); i++ ) {
  77. if ( Buffer[i] == '\n' ) {
  78. Buffer[i] = '\0';
  79. break;
  80. }
  81. }
  82. Response = Buffer[ 0 ];
  83. switch( Response ) {
  84. case 'Y':
  85. case 'y':
  86. ret = TRUE;
  87. keepGoing = FALSE;
  88. break;
  89. case EOF:
  90. fprintf( stderr,
  91. "EOF at console. I assume you mean no.\n" );
  92. // fallthrough
  93. case 'N':
  94. case 'n':
  95. ret = FALSE;
  96. keepGoing = FALSE;
  97. break;
  98. default:
  99. printf( "Your response, %02x ('%c'), doesn't make sense.\n"
  100. "'Y' and 'N' are the only acceptable responses.",
  101. Response,
  102. Response );
  103. }
  104. } while ( keepGoing );
  105. if ( !ret ) {
  106. printf( "Exiting.\n" );
  107. exit( -1 );
  108. }
  109. return ret;
  110. }
  111. BOOL
  112. GetTargetDomainFromUser( IN LPSTR UserName,
  113. OUT LPSTR *ppRealUserName,
  114. OUT OPTIONAL LPWSTR *ppTargetDC ) {
  115. HANDLE hDS;
  116. DWORD dwErr;
  117. DWORD StringLength;
  118. BOOL ret = FALSE;
  119. PDS_NAME_RESULTA pResults;
  120. LPWSTR DcName; /* BUGBUG: this implementation takes ANSI
  121. parameters and converts them to unicode.
  122. This is an artifact of this being a
  123. proof-of-concept app that later became a
  124. support tool.
  125. Someday, we should use unicode throughout and
  126. convert to ANSI as needed. */
  127. PDOMAIN_CONTROLLER_INFO pDCName;
  128. LPSTR DomainName;
  129. LPSTR Cursor;
  130. ASSERT( ppRealUserName != NULL );
  131. *ppRealUserName = UserName;
  132. if (ppTargetDC) {
  133. *ppTargetDC = NULL;
  134. }
  135. dwErr = DsBind( NULL, NULL, &hDS );
  136. if ( dwErr != ERROR_SUCCESS ) {
  137. fprintf( stderr,
  138. "Cannot bind to default domain: 0x%x\n",
  139. dwErr );
  140. } else {
  141. dwErr = DsCrackNamesA( hDS,
  142. DS_NAME_NO_FLAGS,
  143. DS_UNKNOWN_NAME,
  144. DS_NT4_ACCOUNT_NAME,
  145. 1,
  146. &UserName,
  147. &pResults );
  148. DsUnBind( hDS );
  149. if ( dwErr == ERROR_FILE_NOT_FOUND ) {
  150. fprintf( stderr,
  151. "Cannot locate the user %hs. Will try the local domain.\n",
  152. UserName );
  153. ret = TRUE;
  154. } else if ( dwErr != ERROR_SUCCESS ) {
  155. fprintf( stderr,
  156. "Cannot DsCrackNames %hs: 0x%x\n",
  157. UserName,
  158. dwErr );
  159. } else {
  160. if ( pResults->cItems != 1 ) {
  161. fprintf( stderr,
  162. "\"%hs\" has %ld matches -- it needs to be unique!\n",
  163. UserName,
  164. pResults->cItems );
  165. } else if ( pResults->rItems[0].status != DS_NAME_NO_ERROR ) {
  166. fprintf( stderr,
  167. "DsCrackNames returned 0x%x in the name entry for %hs.\n",
  168. pResults->rItems[ 0 ].status,
  169. UserName );
  170. } else {
  171. DomainName = pResults->rItems[0].pDomain;
  172. Cursor = strchr( pResults->rItems[ 0 ].pName, '\\' );
  173. ASSERT( Cursor != NULL ); /* dscracknames wouldn't give back
  174. an NT4_ACCOUNT_NAME that is not
  175. of the form DOMAIN\user */
  176. Cursor++;
  177. *ppRealUserName = _strdup( Cursor );
  178. if ( !*ppRealUserName ) {
  179. /* Note that I'm reading from the output parameter after
  180. writing to it, which might be dangerous if this weren't
  181. just an app. */
  182. fprintf( stderr,
  183. "Couldn't return username portion of \"%hs\""
  184. " -- out of memory.\n",
  185. pResults->rItems[0].pName );
  186. } else if ( !ppTargetDC ) {
  187. // user has already selected a DC,
  188. // so he doesn't need us to hunt for one.
  189. ret = TRUE;
  190. } else {
  191. // next, hunt for a DC in that domain.
  192. dwErr = DsGetDcNameA( NULL, // perform locally
  193. DomainName,
  194. NULL, // domain GUID: don't care
  195. NULL, // site name: use closest site
  196. DS_DIRECTORY_SERVICE_REQUIRED |
  197. DS_RETURN_DNS_NAME |
  198. DS_WRITABLE_REQUIRED,
  199. &pDCName );
  200. if ( dwErr != ERROR_SUCCESS ) {
  201. fprintf( stderr,
  202. "Cannot DsGetDcName for \"%hs\": 0x%x\n",
  203. DomainName,
  204. dwErr );
  205. } else {
  206. while( pDCName->DomainControllerName[0] == '\\' ) {
  207. pDCName->DomainControllerName++;
  208. }
  209. /* Retrieve the string length, +1 for terminating null. */
  210. StringLength = strlen( pDCName->DomainControllerName ) +1;
  211. DcName = (LPWSTR) malloc( StringLength * sizeof( WCHAR ) );
  212. if ( !DcName ) {
  213. fprintf( stderr,
  214. "cannot allocate %ld WCHARs.",
  215. StringLength );
  216. } else {
  217. swprintf( DcName,
  218. L"%hs",
  219. pDCName->DomainControllerName );
  220. *ppTargetDC = DcName;
  221. printf( "Targeting domain controller: %ws\n",
  222. DcName );
  223. ret = TRUE;
  224. }
  225. NetApiBufferFree( pDCName );
  226. }
  227. if ( !ret ) {
  228. free( *ppRealUserName );
  229. }
  230. }
  231. }
  232. DsFreeNameResult( pResults );
  233. }
  234. }
  235. if ( !ret ) {
  236. *ppRealUserName = UserName;
  237. }
  238. return ret;
  239. }
  240. VOID
  241. GetKeyVersionFromDomain( IN PLDAP pLdap,
  242. IN LPSTR UserName,
  243. IN OUT PULONG pkvno ) {
  244. ASSERT( pLdap != NULL );
  245. if ( *pkvno == KVNO_DETECT_AT_DC ) {
  246. if ( !LdapQueryUlongAttributeA( pLdap,
  247. NULL, // ignored
  248. UserName,
  249. KvnoAttribute,
  250. pkvno ) ) {
  251. // a win2k DC would fail with attribute not found.
  252. if ( GetLastError() == LDAP_NO_SUCH_ATTRIBUTE ) {
  253. fprintf(
  254. stderr,
  255. "The %hs attribute does not exist on the target DC.\n"
  256. " Assuming this is a Windows 2000 domain, and setting\n"
  257. " the Key Version Number in the Keytab to 1.\n"
  258. "\n"
  259. " Supply \"/kvno 1\" on the command line to skip this message.\n",
  260. KvnoAttribute );
  261. *pkvno = 1;
  262. } else {
  263. fprintf( stderr,
  264. "Failed to query kvno attribute from the DC.\n"
  265. "Ktpass cannot continue.\n" );
  266. exit( -1 );
  267. }
  268. }
  269. }
  270. }
  271. VOID
  272. CheckKeyVersion( IN ULONG BigVer ) {
  273. BYTE LittleVer;
  274. LittleVer = (BYTE) BigVer;
  275. if ( LittleVer != BigVer ) {
  276. if ( !UserWantsToDoItAnyway(
  277. "WARNING: The Key version used by Windows (%ld) is too big\n"
  278. " to be encoded in a keytab without truncating it to %ld.\n"
  279. " This is due to a limitation of the keytab file format\n"
  280. " and may lead to interoperability issues.\n"
  281. "\n"
  282. "Do you want to proceed and truncate the version number",
  283. BigVer,
  284. LittleVer ) ) {
  285. exit( -1 );
  286. }
  287. }
  288. }
  289. extern BOOL KtDumpSalt; // in ..\lib\mkkey.c
  290. extern LPWSTR RawHash; // in mkkey.c
  291. // #include "globals.h"
  292. // #include "commands.h"
  293. int __cdecl
  294. main( int argc,
  295. PCHAR argv[] ) {
  296. LPSTR Principal = NULL;
  297. LPSTR UserName = NULL;
  298. LPSTR Password = NULL;
  299. PLDAP pLdap = NULL;
  300. LPSTR UserDn = NULL;
  301. BOOL SetUpn = TRUE;
  302. ULONG BigKvno = KVNO_DETECT_AT_DC;
  303. ULONG Crypto = KERB_ETYPE_DES_CBC_MD5;
  304. ULONG ptype = KRB5_NT_PRINCIPAL;
  305. ULONG uacFlags = 0;
  306. PKTFILE pktFile = NULL;
  307. PCHAR KtReadFile = NULL;
  308. PCHAR KtWriteFile = NULL;
  309. BOOL DesOnly = TRUE;
  310. ULONG LdapOperation = LDAP_MOD_ADD;
  311. HANDLE hConsole = NULL;
  312. BOOL SetPassword = TRUE;
  313. BOOL WarnedAboutAccountStrangeness = FALSE;
  314. PVOID pvTrash = NULL;
  315. DWORD dwConsoleMode;
  316. LPWSTR BindTarget = NULL; // local domain (see ldlib\delegtools.c)
  317. optEnumStruct CryptoSystems[] = {
  318. { "DES-CBC-CRC", (PVOID) KERB_ETYPE_DES_CBC_CRC, "for compatibility" },
  319. { "DES-CBC-MD5", (PVOID) KERB_ETYPE_DES_CBC_MD5, "default" },
  320. TERMINATE_ARRAY
  321. };
  322. #define DUPE( type, desc ) { "KRB5_NT_" # type, \
  323. (PVOID) KRB5_NT_##type, \
  324. desc }
  325. optEnumStruct PrincTypes[] = {
  326. DUPE( PRINCIPAL, "The general ptype-- recommended" ),
  327. DUPE( SRV_INST, "user service instance" ),
  328. DUPE( SRV_HST, "host service instance" ),
  329. DUPE( SRV_XHST, NULL ),
  330. TERMINATE_ARRAY
  331. };
  332. optEnumStruct MappingOperations[] = {
  333. { "add", (PVOID) LDAP_MOD_ADD, "add value (default)" },
  334. { "set", (PVOID) LDAP_MOD_REPLACE, "set value" },
  335. TERMINATE_ARRAY
  336. };
  337. #if DBG
  338. #undef OPT_HIDDEN
  339. #define OPT_HIDDEN 0 /* no hidden options on debug builds. */
  340. #endif
  341. optionStruct Options[] = {
  342. { "?", NULL, OPT_HELP | OPT_HIDDEN },
  343. { "h", NULL, OPT_HELP | OPT_HIDDEN },
  344. { "help", NULL, OPT_HELP | OPT_HIDDEN },
  345. { NULL, NULL, OPT_DUMMY, "most useful args" },
  346. { "out", &KtWriteFile, OPT_STRING, "Keytab to produce" },
  347. { "princ", &Principal, OPT_STRING, "Principal name (user@REALM)" },
  348. { "pass", &Password, OPT_STRING, "password to use" },
  349. { NULL, NULL, OPT_CONTINUE, "use \"*\" to prompt for password." },
  350. { NULL, NULL, OPT_DUMMY, "less useful stuff" },
  351. { "mapuser", &UserName, OPT_STRING, "map princ (above) to this user account (default: don't)" },
  352. { "mapOp", &LdapOperation, OPT_ENUMERATED, "how to set the mapping attribute (default: add it)", MappingOperations },
  353. { "DesOnly", &DesOnly, OPT_BOOL, "Set account for des-only encryption (default:do)" },
  354. { "in", &KtReadFile, OPT_STRING, "Keytab to read/digest" },
  355. { NULL, NULL, OPT_DUMMY, "options for key generation" },
  356. { "crypto", &Crypto, OPT_ENUMERATED, "Cryptosystem to use", CryptoSystems },
  357. { "ptype", &ptype, OPT_ENUMERATED, "principal type in question", PrincTypes },
  358. { "kvno", &BigKvno, OPT_INT, "Override Key Version Number"},
  359. { NULL, NULL, OPT_CONTINUE, "Default: query DC for kvno. Use /kvno 1 for Win2K compat." },
  360. /* It is best NOT to mess with the keytab version number.
  361. We use this for debugging only. */
  362. /* Use /target to hit a specific DC. This is good if you just
  363. created a user there, for example. It also eliminates the
  364. network traffic used to locate the DC */
  365. { "Answer", &PromptResponse, OPT_BOOL, "+Answer answers YES to prompts. -Answer answers NO." },
  366. { "Target", &BindTarget, OPT_WSTRING, "Which DC to use. Default:detect" },
  367. { "ktvno", &ktvno, OPT_INT | OPT_HIDDEN, "keytab version (def 0x502). Leave this alone." },
  368. // { "Debug", &DebugFlag, OPT_BOOL | OPT_HIDDEN },
  369. { "RawSalt", &RawHash, OPT_WSTRING | OPT_HIDDEN, "raw salt to use when generating key (not needed)" },
  370. { "DumpSalt", &KtDumpSalt, OPT_BOOL | OPT_HIDDEN, "show us the MIT salt being used to generate the key" },
  371. { "SetUpn", &SetUpn, OPT_BOOL | OPT_HIDDEN, "Set the UPN in addition to the SPN. Default DO." },
  372. { "SetPass", &SetPassword, OPT_BOOL | OPT_HIDDEN, "Set the user's password if supplied." },
  373. TERMINATE_ARRAY
  374. };
  375. FILE *f;
  376. // DebugFlag = 0;
  377. ParseOptionsEx( argc-1,
  378. argv+1,
  379. Options,
  380. OPT_FLAG_TERMINATE,
  381. &pvTrash,
  382. NULL,
  383. NULL );
  384. if ( ( Principal ) &&
  385. ( strlen( Principal ) > BUFFER_SIZE ) ) {
  386. fprintf( stderr,
  387. "Please submit a shorter principal name.\n" );
  388. return 1;
  389. }
  390. if ( Password &&
  391. ( strlen( Password ) > BUFFER_SIZE ) ) {
  392. fprintf( stderr,
  393. "Please submit a shorter password.\n" );
  394. return 1;
  395. }
  396. if ( KtReadFile ) {
  397. if ( ReadKeytabFromFile( &pktFile, KtReadFile ) ) {
  398. fprintf( stderr,
  399. "Existing keytab: \n\n" );
  400. DisplayKeytab( stderr, pktFile, 0xFFFFFFFF );
  401. } else {
  402. fprintf( stderr,
  403. "Keytab read failed!\n" );
  404. return 5;
  405. }
  406. }
  407. if ( !UserName &&
  408. ( BigKvno == KVNO_DETECT_AT_DC ) ) {
  409. //
  410. // if the user doesn't pass /kvno, we want to
  411. // detect the kvno at the DC. However, if no
  412. // /mapuser is passed, there's no DC to do this
  413. // at. Win2K ktpass provided '1' as the default,
  414. // so this is what we do here.
  415. //
  416. BigKvno = 1;
  417. }
  418. if ( Principal ) {
  419. LPSTR realm, cp;
  420. CHAR tempBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  421. realm = strchr( Principal, '@' );
  422. if ( realm ) {
  423. ULONG length;
  424. realm++;
  425. length = lstrlenA( realm );
  426. if ( length >= sizeof( tempBuffer )) {
  427. length = sizeof( tempBuffer ) - 1;
  428. }
  429. memcpy( tempBuffer, realm, ( length + 1 ) * sizeof( realm[0] ) );
  430. tempBuffer[sizeof( tempBuffer ) - 1] = '\0';
  431. CharUpperBuffA( realm, length );
  432. if ( lstrcmpA( realm, tempBuffer ) != 0 ) {
  433. fprintf( stderr,
  434. "WARNING: realm \"%hs\" has lowercase characters in it.\n"
  435. " We only currently support realms in UPPERCASE.\n"
  436. " assuming you mean \"%hs\"...\n",
  437. tempBuffer, realm );
  438. // now "realm" will be all uppercase.
  439. }
  440. *(realm-1) = '\0'; // separate the realm from the principal
  441. if ( UserName ) {
  442. /* Crack the domain name (507151). Without this call
  443. the DC we target may not contain the user object.
  444. Note that UserName is modified by this operation. */
  445. if ( !GetTargetDomainFromUser( UserName,
  446. &UserName,
  447. BindTarget ?
  448. NULL :
  449. &BindTarget ) ) {
  450. return 1;
  451. }
  452. // connect to the DSA.
  453. if ( pLdap ||
  454. ConnectAndBindToDefaultDsa( BindTarget,
  455. &pLdap ) ) {
  456. // locate the User
  457. if ( UserDn ||
  458. FindUser( pLdap,
  459. UserName,
  460. &uacFlags,
  461. &UserDn ) ) {
  462. if ( ( LdapOperation == LDAP_MOD_REPLACE ) &
  463. !( uacFlags & UF_NORMAL_ACCOUNT ) ) {
  464. /* 97282: the user is not UF_NORMAL_ACCOUNT, so
  465. check to see that the caller *really* wants to
  466. blow away the non-user's SPNs. */
  467. if ( uacFlags ) {
  468. fprintf( stderr,
  469. "WARNING: Account %hs is not a normal user "
  470. "account (uacFlags=0x%x).\n",
  471. UserName,
  472. uacFlags );
  473. } else {
  474. fprintf( stderr,
  475. "WARNING: Cannot determine the account type"
  476. " for %hs.\n",
  477. UserName );
  478. }
  479. WarnedAboutAccountStrangeness = TRUE;
  480. if ( !UserWantsToDoItAnyway(
  481. "Do you really want to delete any previous "
  482. "servicePrincipalName values on %hs",
  483. UserName ) ) {
  484. /* Abort the operation, but try to do whatever
  485. else the user asked us to do. */
  486. goto abortedMapping;
  487. }
  488. }
  489. /* 97279: check to see if there are other SPNs
  490. by the same name already registered. If so,
  491. we don't want to blow away those accounts.
  492. If/when we decide to do this, we'd do it here. */
  493. // set/add the user property
  494. if ( SetStringProperty( pLdap,
  495. UserDn,
  496. "servicePrincipalName",
  497. Principal,
  498. LdapOperation ) ) {
  499. if ( SetUpn ) {
  500. *(realm-1) = '@'; // UPN includes the '@'
  501. if ( !SetStringProperty( pLdap,
  502. UserDn,
  503. "userPrincipalName",
  504. Principal,
  505. LDAP_MOD_REPLACE ) ) {
  506. fprintf( stderr,
  507. "WARNING: Failed to set UPN %hs on %hs.\n"
  508. " kinits to '%hs' will fail.\n",
  509. Principal,
  510. UserDn,
  511. Principal );
  512. }
  513. *(realm -1 ) = '\0'; // where it was before
  514. }
  515. fprintf( stderr,
  516. "Successfully mapped %hs to %hs.\n",
  517. Principal,
  518. UserName );
  519. abortedMapping:
  520. ; /* Need a semicolon so we can goto here. */
  521. } else {
  522. fprintf( stderr,
  523. "WARNING: Unable to set SPN mapping data.\n"
  524. " If %hs already has an SPN mapping installed for "
  525. " %hs, this is no cause for concern.\n",
  526. UserName,
  527. Principal );
  528. }
  529. } // else a message will be printed.
  530. } // else a message will be printed.
  531. } // if ( UserName )
  532. if ( Password ) {
  533. PKTENT pktEntry;
  534. CHAR TempPassword[ 255 ], ConfirmPassword[ 255 ];
  535. if ( lstrcmpA( Password, "*" ) == 0 ) {
  536. hConsole = GetStdHandle( STD_INPUT_HANDLE );
  537. if ( GetConsoleMode( hConsole,
  538. &dwConsoleMode ) ) {
  539. if ( SetConsoleMode( hConsole,
  540. dwConsoleMode & ~ENABLE_ECHO_INPUT ) ) {
  541. do {
  542. fprintf( stderr,
  543. "Type the password for %hs: ",
  544. Principal );
  545. if ( !fgets( TempPassword,
  546. sizeof( TempPassword ),
  547. stdin ) ) {
  548. fprintf( stderr,
  549. "failed to read password.\n" );
  550. exit( GetLastError() );
  551. }
  552. fprintf( stderr,
  553. "\nType the password again to confirm:" );
  554. if ( !fgets( ConfirmPassword,
  555. sizeof( ConfirmPassword ),
  556. stdin ) ) {
  557. fprintf( stderr,
  558. "failed to read confirmation password.\n" );
  559. exit( GetLastError() );
  560. }
  561. if ( lstrcmpA( ConfirmPassword,
  562. TempPassword ) == 0 ) {
  563. printf( "\n" );
  564. break;
  565. } else {
  566. fprintf( stderr,
  567. "The passwords you type must match exactly.\n" );
  568. }
  569. } while ( TRUE );
  570. Password = TempPassword;
  571. SetConsoleMode( hConsole, dwConsoleMode );
  572. } else {
  573. fprintf( stderr,
  574. "Failed to turn off echo input for password entry:"
  575. " 0x%x\n",
  576. GetLastError() );
  577. return -1;
  578. }
  579. } else {
  580. fprintf( stderr,
  581. "Failed to retrieve console mode settings: 0x%x.\n",
  582. GetLastError() );
  583. return -1;
  584. }
  585. }
  586. if ( SetPassword && UserName ) {
  587. DWORD err;
  588. NET_API_STATUS nas;
  589. PUSER_INFO_1 pUserInfo;
  590. WCHAR wUserName[ MAX_PATH ];
  591. DOMAIN_CONTROLLER_INFOW * DomainControllerInfo = NULL;
  592. /* WASBUG 369: converting ascii to unicode
  593. This is safe, because RFC1510 doesn't do
  594. UNICODE, and this tool is specifically for
  595. unix interop support; unix machines don't
  596. do unicode. */
  597. if ( strlen( UserName ) >= MAX_PATH ) {
  598. UserName[MAX_PATH] = '\0';
  599. }
  600. wsprintfW( wUserName,
  601. L"%hs",
  602. UserName );
  603. nas = NetUserGetInfo( BindTarget,
  604. wUserName,
  605. 1, // level 1
  606. (PBYTE *) &pUserInfo );
  607. if ( nas == NERR_Success ) {
  608. WCHAR wPassword[ PWLEN ];
  609. uacFlags = pUserInfo->usri1_flags;
  610. if ( !( uacFlags & UF_NORMAL_ACCOUNT ) ) {
  611. /* 97282: For abnormal accounts (these include
  612. workstation trust accounts, interdomain
  613. trust accounts, server trust accounts),
  614. ask the user if he/she really wants to
  615. perform this operation. */
  616. if ( !WarnedAboutAccountStrangeness ) {
  617. fprintf( stderr,
  618. "WARNING: Account %hs is not a user account"
  619. " (uacflags=0x%x).\n",
  620. UserName,
  621. uacFlags );
  622. WarnedAboutAccountStrangeness = TRUE;
  623. }
  624. fprintf( stderr,
  625. "WARNING: Resetting %hs's password may"
  626. " cause authentication problems if %hs"
  627. " is being used as a server.\n",
  628. UserName,
  629. UserName );
  630. if ( !UserWantsToDoItAnyway( "Reset %hs's password",
  631. UserName ) ) {
  632. /* Skip it, but try to do anything else the user
  633. requested. */
  634. goto skipSetPassword;
  635. }
  636. }
  637. if ( strlen( Password ) >= PWLEN ) {
  638. Password[PWLEN] = '\0';
  639. }
  640. wsprintfW( wPassword,
  641. L"%hs",
  642. Password );
  643. pUserInfo->usri1_password = wPassword;
  644. nas = NetUserSetInfo( BindTarget,
  645. wUserName,
  646. 1, // level 1
  647. (LPBYTE) pUserInfo,
  648. NULL );
  649. if ( nas == NERR_Success ) {
  650. skipSetPassword:
  651. NetApiBufferFree( pUserInfo );
  652. GetKeyVersionFromDomain( pLdap,
  653. UserName,
  654. &BigKvno );
  655. goto skipout;
  656. } else {
  657. fprintf( stderr,
  658. "Failed to set password for %ws: 0x%x.\n",
  659. wUserName,
  660. nas );
  661. }
  662. } else {
  663. fprintf( stderr,
  664. "Failed to retrieve user info for %ws: 0x%x.\n",
  665. wUserName,
  666. nas );
  667. }
  668. fprintf( stderr,
  669. "Aborted.\n" );
  670. return nas;
  671. }
  672. skipout:
  673. ASSERT( realm != NULL );
  674. // physically separate the realm data.
  675. ASSERT( *( realm -1 ) == '\0' );
  676. CheckKeyVersion( BigKvno );
  677. if ( KtCreateKey( &pktEntry,
  678. Principal,
  679. Password,
  680. realm,
  681. (K5_OCTET) BigKvno,
  682. ptype,
  683. Crypto, // this is the "fake" etype
  684. Crypto ) ) {
  685. if ( pktFile == NULL ) {
  686. pktFile = NewKt();
  687. if ( !pktFile ) {
  688. fprintf( stderr,
  689. "Failed to allocate keytable.\n" );
  690. return 4;
  691. }
  692. }
  693. if ( AddEntryToKeytab( pktFile,
  694. pktEntry,
  695. FALSE ) ) {
  696. fprintf( stderr,
  697. "Key created.\n" );
  698. } else {
  699. fprintf( stderr,
  700. "Failed to add entry to keytab.\n" );
  701. return 2;
  702. }
  703. if ( KtWriteFile ) {
  704. fprintf( stderr,
  705. "Output keytab to %hs:\n",
  706. KtWriteFile );
  707. DisplayKeytab( stderr, pktFile, 0xFFFFFFFF );
  708. if ( !WriteKeytabToFile( pktFile, KtWriteFile ) ) {
  709. fprintf( stderr, "\n\n"
  710. "Failed to write keytab file %hs.\n",
  711. KtWriteFile );
  712. return 6;
  713. }
  714. // write keytab.
  715. }
  716. } else {
  717. fprintf( stderr,
  718. "Failed to create key for keytab. Quitting.\n" );
  719. return 7;
  720. }
  721. if ( UserName && DesOnly ) {
  722. ASSERT( pLdap != NULL );
  723. ASSERT( UserDn != NULL );
  724. // set the DES_ONLY flag
  725. // first, query the account's account flags.
  726. if ( uacFlags /* If we already queried the user's
  727. AccountControl flags, no need to do it
  728. again */
  729. || QueryAccountControlFlagsA( pLdap,
  730. NULL, // domain name is ignored
  731. UserName,
  732. &uacFlags ) ) {
  733. uacFlags |= UF_USE_DES_KEY_ONLY;
  734. if ( SetAccountControlFlagsA( pLdap,
  735. NULL, // domain name is ignored
  736. UserName,
  737. uacFlags ) ) {
  738. fprintf( stderr,
  739. "Account %hs has been set for DES-only encryption.\n",
  740. UserName );
  741. if ( !SetPassword ) {
  742. fprintf( stderr,
  743. "To make this take effect, you must change "
  744. "%hs's password manually.\n",
  745. UserName );
  746. }
  747. } // else message printed.
  748. } // else message printed
  749. }
  750. } // else user doesn't want me to make a key
  751. if ( !Password && !UserName ) {
  752. fprintf( stderr,
  753. "doing nothing.\n"
  754. "specify /pass and/or /mapuser to either \n"
  755. "make a key with the given password or \n"
  756. "map a user to a particular SPN, respectively.\n" );
  757. }
  758. } else {
  759. fprintf( stderr,
  760. "principal %hs doesn't contain an '@' symbol.\n"
  761. "Looking for something of the form:\n"
  762. " [email protected] or xyz/[email protected] \n"
  763. " ^ ^\n"
  764. " | |\n"
  765. " +--------------------+---- I didn't find these.\n",
  766. Principal );
  767. return 1;
  768. }
  769. } else {
  770. //
  771. // if no principal is specified, we should find a way to warn
  772. // the user. The only real reason to do this is when importing
  773. // a keytab and not saving a key; admittedly not a likely scenario.
  774. //
  775. printf( "\n"
  776. "WARNING: No principal name specified.\n" );
  777. }
  778. return 0;
  779. }