2525
2626#include "gss_ntlmssp.h"
2727
28+ static int hex_to_key (const char * hex , struct ntlm_key * key )
29+ {
30+ size_t len = strlen (hex );
31+ if (len != 32 ) return ERR_KEYLEN ;
32+
33+ for (int i = 0 ; i < 16 ; i ++ ) {
34+ key -> data [i ] = 0 ;
35+ for (int j = 0 ; j < 2 ; j ++ ) {
36+ uint8_t c = hex [i * 2 + j ];
37+ if (c >= '0' && c <= '9' ) {
38+ c -= '0' ;
39+ } else if (c >= 'A' && c <= 'F' ) {
40+ c -= 'A' - 10 ;
41+ } else if (c >= 'a' && c <= 'f' ) {
42+ c -= 'a' - 10 ;
43+ } else {
44+ return ERR_BADARG ;
45+ }
46+ key -> data [i ] |= (c << ((1 - j ) * 4 ));
47+ }
48+ }
49+ key -> length = 16 ;
50+ return 0 ;
51+ }
52+
2853static int get_user_file_creds (struct gssntlm_name * name ,
2954 struct gssntlm_cred * cred )
3055{
56+ struct gssntlm_ctx * ctx ;
57+ int lm_compat_lvl = -1 ;
3158 const char * envvar ;
3259 char line [1024 ];
33- char * dom , * usr , * pwd ;
60+ char * field1 , * field2 , * field3 , * field4 ;
61+ char * dom , * usr , * pwd , * lm , * nt ;
3462 char * p ;
3563 bool found = false;
3664 FILE * f ;
3765 int ret ;
3866
67+ ctx = calloc (1 , sizeof (struct gssntlm_ctx ));
68+ if (!ctx ) return ENOMEM ;
69+
70+ lm_compat_lvl = gssntlm_get_lm_compatibility_level ();
71+ if (!gssntlm_required_security (lm_compat_lvl , ctx )) return ERR_BADLMLVL ;
72+
3973 /* use the same var used by Heimdal */
4074 envvar = getenv ("NTLM_USER_FILE" );
4175 if (envvar == NULL ) return ENOENT ;
@@ -44,30 +78,78 @@ static int get_user_file_creds(struct gssntlm_name *name,
4478 * some compatibility between implementations:
4579 * Each line is one entry like the following:
4680 * DOMAIN:USERNAME:PASSWORD */
81+
82+ /* **OR** */
83+
84+ /* Use the smbpasswd file format for Samba compatibility.
85+ * Each line is one entry like the following:
86+ * NAME:UID:LM_HASH:NT_HASH:ACCT_FLAGS:TIMESTAMP
87+ * Fields after NT_HASH are ignored
88+ *
89+ * The smbpasswd format is extended to allow domain qualified names.
90+ */
91+
92+ /* The second format is distinguished from the first based on
93+ * the number of fields encountered. It is technically possible
94+ * to mix both formats in a single file though it is not
95+ * recommended.
96+ */
97+
4798 f = fopen (envvar , "r" );
4899 if (!f ) return errno ;
49100
50101 while (fgets (line , 1024 , f )) {
51102 p = line ;
52103 if (* p == '#' ) continue ;
53- dom = p ;
54- p = strchr (dom , ':' );
104+ field1 = p ;
105+ p = strchr (field1 , ':' );
55106 if (!p ) continue ;
56107 * p ++ = '\0' ;
57- usr = p ;
58- p = strchr (usr , ':' );
108+ field2 = p ;
109+ p = strchr (field2 , ':' );
59110 if (!p ) continue ;
60111 * p ++ = '\0' ;
61- pwd = p ;
62- strsep (& p , "\r\n" );
112+ field3 = p ;
113+ p = strchr (field3 , ':' );
114+ if (!p ) {
115+ /* Assume Heimdal file format */
116+ p = field3 ;
117+ strsep (& p , "\r\n" );
118+
119+ dom = field1 ;
120+ usr = field2 ;
121+ pwd = field3 ;
122+ lm = NULL ;
123+ nt = NULL ;
124+ } else {
125+ * p ++ = '\0' ;
126+ field4 = p ;
127+ p = strchr (field4 , ':' );
128+ if (!p ) continue ;
129+ * p ++ = '\0' ;
130+ /* Assume smbpasswd file format */
131+ dom = NULL ;
132+ usr = field1 ;
133+ pwd = NULL ;
134+ lm = field3 ;
135+ nt = field4 ;
136+
137+ /* check if username is domain qualified */
138+ p = strchr (usr , '\\' );
139+ if (p ) {
140+ dom = usr ;
141+ * p ++ = '\0' ;
142+ usr = p ;
143+ }
144+ }
63145
64146 /* if no name is specified use the first found */
65147 if (name == NULL ) {
66148 found = true;
67149 break ;
68150 }
69151
70- if (name -> data .user .domain ) {
152+ if (name -> data .user .domain && dom ) {
71153 if (!ntlm_casecmp (dom , name -> data .user .domain )) continue ;
72154 }
73155 if (name -> data .user .name ) {
@@ -86,19 +168,34 @@ static int get_user_file_creds(struct gssntlm_name *name,
86168
87169 cred -> type = GSSNTLM_CRED_USER ;
88170 cred -> cred .user .user .type = GSSNTLM_NAME_USER ;
89- cred -> cred .user .user .data .user .domain = strdup (dom );
90- if (!cred -> cred .user .user .data .user .domain ) return ENOMEM ;
171+ if (dom ) {
172+ cred -> cred .user .user .data .user .domain = strdup (dom );
173+ if (!cred -> cred .user .user .data .user .domain ) return ENOMEM ;
174+ }
91175 cred -> cred .user .user .data .user .name = strdup (usr );
92176 if (!cred -> cred .user .user .data .user .name ) return ENOMEM ;
93- cred -> cred .user .nt_hash .length = 16 ;
94177
95- ret = NTOWFv1 (pwd , & cred -> cred . user . nt_hash );
96- if ( ret ) return ret ;
178+ if (pwd ) {
179+ cred -> cred . user . nt_hash . length = 16 ;
97180
98- if (gssntlm_get_lm_compatibility_level () < 3 ) {
99- cred -> cred .user .lm_hash .length = 16 ;
100- ret = LMOWFv1 (pwd , & cred -> cred .user .lm_hash );
181+ ret = NTOWFv1 (pwd , & cred -> cred .user .nt_hash );
101182 if (ret ) return ret ;
183+
184+ if (gssntlm_sec_lm_ok (ctx )) {
185+ cred -> cred .user .lm_hash .length = 16 ;
186+ ret = LMOWFv1 (pwd , & cred -> cred .user .lm_hash );
187+ if (ret ) return ret ;
188+ }
189+ }
190+
191+ if (lm && nt ) {
192+ ret = hex_to_key (nt , & cred -> cred .user .nt_hash );
193+ if (ret ) return ret ;
194+
195+ if (gssntlm_sec_lm_ok (ctx )) {
196+ ret = hex_to_key (lm , & cred -> cred .user .lm_hash );
197+ if (ret ) return ret ;
198+ }
102199 }
103200
104201 return 0 ;
@@ -134,34 +231,6 @@ static int get_server_creds(struct gssntlm_name *name,
134231 return ret ;
135232}
136233
137- static int hex_to_key (const char * hex , struct ntlm_key * key )
138- {
139- const char * p ;
140- uint32_t i , j ;
141- uint8_t t ;
142- size_t len ;
143-
144- len = strlen (hex );
145- if (len != 32 ) return EINVAL ;
146-
147- for (i = 0 ; i < 16 ; i ++ ) {
148- for (j = 0 ; j < 2 ; j ++ ) {
149- p = & hex [j + (i * 2 )];
150- if (* p >= '0' && * p <= '9' ) {
151- t = (* p - '0' );
152- } else if (* p >= 'A' && * p <= 'F' ) {
153- t = (* p - 'A' + 10 );
154- } else {
155- return EINVAL ;
156- }
157- if (j == 0 ) t = t << 4 ;
158- key -> data [i ] = t ;
159- }
160- }
161- key -> length = 16 ;
162- return 0 ;
163- }
164-
165234#define GENERIC_CS_PASSWORD "password"
166235/* To support in future, RC4 Key is NT hash */
167236#define KRB5_CS_CLI_KEYTAB_URN "client_keytab"
0 commit comments