iPXE
ntlm.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /** @file
27  *
28  * NT LAN Manager (NTLM) authentication
29  *
30  */
31 
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <byteswap.h>
37 #include <ipxe/md4.h>
38 #include <ipxe/md5.h>
39 #include <ipxe/hmac.h>
40 #include <ipxe/ntlm.h>
41 
42 /** Negotiate message
43  *
44  * This message content is fixed since there is no need to specify the
45  * calling workstation name or domain name, and the set of flags is
46  * mandated by the MS-NLMP specification.
47  */
49  .header = {
50  .magic = NTLM_MAGIC,
51  .type = cpu_to_le32 ( NTLM_NEGOTIATE ),
52  },
58 };
59 
60 /**
61  * Parse NTLM Challenge
62  *
63  * @v challenge Challenge message
64  * @v len Length of Challenge message
65  * @v info Challenge information to fill in
66  * @ret rc Return status code
67  */
68 int ntlm_challenge ( struct ntlm_challenge *challenge, size_t len,
69  struct ntlm_challenge_info *info ) {
70  size_t offset;
71 
72  DBGC ( challenge, "NTLM challenge message:\n" );
73  DBGC_HDA ( challenge, 0, challenge, len );
74 
75  /* Sanity checks */
76  if ( len < sizeof ( *challenge ) ) {
77  DBGC ( challenge, "NTLM underlength challenge (%zd bytes)\n",
78  len );
79  return -EINVAL;
80  }
81 
82  /* Extract nonce */
83  info->nonce = &challenge->nonce;
84  DBGC ( challenge, "NTLM challenge nonce:\n" );
85  DBGC_HDA ( challenge, 0, info->nonce, sizeof ( *info->nonce ) );
86 
87  /* Extract target information */
88  info->len = le16_to_cpu ( challenge->info.len );
89  offset = le32_to_cpu ( challenge->info.offset );
90  if ( ( offset > len ) ||
91  ( info->len > ( len - offset ) ) ) {
92  DBGC ( challenge, "NTLM target information outside "
93  "challenge\n" );
94  DBGC_HDA ( challenge, 0, challenge, len );
95  return -EINVAL;
96  }
97  info->target = ( ( ( void * ) challenge ) + offset );
98  DBGC ( challenge, "NTLM challenge target information:\n" );
99  DBGC_HDA ( challenge, 0, info->target, info->len );
100 
101  return 0;
102 }
103 
104 /**
105  * Calculate NTLM verification key
106  *
107  * @v domain Domain name (or NULL)
108  * @v username User name (or NULL)
109  * @v password Password (or NULL)
110  * @v key Key to fill in
111  *
112  * This is the NTOWFv2() function as defined in MS-NLMP.
113  */
114 void ntlm_key ( const char *domain, const char *username,
115  const char *password, struct ntlm_key *key ) {
116  struct digest_algorithm *md4 = &md4_algorithm;
117  struct digest_algorithm *md5 = &md5_algorithm;
118  union {
119  uint8_t md4[MD4_CTX_SIZE];
120  uint8_t md5[MD5_CTX_SIZE];
121  } ctx;
123  size_t digest_len;
124  uint8_t c;
125  uint16_t wc;
126 
127  /* Use empty usernames/passwords if not specified */
128  if ( ! domain )
129  domain = "";
130  if ( ! username )
131  username = "";
132  if ( ! password )
133  password = "";
134 
135  /* Construct MD4 digest of (Unicode) password */
136  digest_init ( md4, ctx.md4 );
137  while ( ( c = *(password++) ) ) {
138  wc = cpu_to_le16 ( c );
139  digest_update ( md4, ctx.md4, &wc, sizeof ( wc ) );
140  }
141  digest_final ( md4, ctx.md4, digest );
142 
143  /* Construct HMAC-MD5 of (Unicode) upper-case username */
144  digest_len = sizeof ( digest );
145  hmac_init ( md5, ctx.md5, digest, &digest_len );
146  while ( ( c = *(username++) ) ) {
147  wc = cpu_to_le16 ( toupper ( c ) );
148  hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) );
149  }
150  while ( ( c = *(domain++) ) ) {
151  wc = cpu_to_le16 ( c );
152  hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) );
153  }
154  hmac_final ( md5, ctx.md5, digest, &digest_len, key->raw );
155  DBGC ( key, "NTLM key:\n" );
156  DBGC_HDA ( key, 0, key, sizeof ( *key ) );
157 }
158 
159 /**
160  * Construct NTLM responses
161  *
162  * @v info Challenge information
163  * @v key Verification key
164  * @v nonce Nonce, or NULL to use a random nonce
165  * @v lm LAN Manager response to fill in
166  * @v nt NT response to fill in
167  */
169  struct ntlm_nonce *nonce, struct ntlm_lm_response *lm,
170  struct ntlm_nt_response *nt ) {
171  struct digest_algorithm *md5 = &md5_algorithm;
172  struct ntlm_nonce tmp_nonce;
174  size_t key_len = sizeof ( *key );
175  unsigned int i;
176 
177  /* Generate random nonce, if needed */
178  if ( ! nonce ) {
179  for ( i = 0 ; i < sizeof ( tmp_nonce ) ; i++ )
180  tmp_nonce.raw[i] = random();
181  nonce = &tmp_nonce;
182  }
183 
184  /* Construct LAN Manager response */
185  memcpy ( &lm->nonce, nonce, sizeof ( lm->nonce ) );
186  hmac_init ( md5, ctx, key->raw, &key_len );
187  hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) );
188  hmac_update ( md5, ctx, &lm->nonce, sizeof ( lm->nonce ) );
189  hmac_final ( md5, ctx, key->raw, &key_len, lm->digest );
190  DBGC ( key, "NTLM LAN Manager response:\n" );
191  DBGC_HDA ( key, 0, lm, sizeof ( *lm ) );
192 
193  /* Construct NT response */
194  memset ( nt, 0, sizeof ( *nt ) );
195  nt->version = NTLM_VERSION_NTLMV2;
196  nt->high = NTLM_VERSION_NTLMV2;
197  memcpy ( &nt->nonce, nonce, sizeof ( nt->nonce ) );
198  hmac_init ( md5, ctx, key->raw, &key_len );
199  hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) );
200  hmac_update ( md5, ctx, &nt->version,
201  ( sizeof ( *nt ) -
202  offsetof ( typeof ( *nt ), version ) ) );
203  hmac_update ( md5, ctx, info->target, info->len );
204  hmac_update ( md5, ctx, &nt->zero, sizeof ( nt->zero ) );
205  hmac_final ( md5, ctx, key->raw, &key_len, nt->digest );
206  DBGC ( key, "NTLM NT response prefix:\n" );
207  DBGC_HDA ( key, 0, nt, sizeof ( *nt ) );
208 }
209 
210 /**
211  * Append data to NTLM message
212  *
213  * @v header Message header, or NULL to only calculate next payload
214  * @v data Data descriptor
215  * @v payload Data payload
216  * @v len Length of data
217  * @ret payload Next data payload
218  */
219 static void * ntlm_append ( struct ntlm_header *header, struct ntlm_data *data,
220  void *payload, size_t len ) {
221 
222  /* Populate data descriptor */
223  if ( header ) {
224  data->offset = cpu_to_le32 ( payload - ( ( void * ) header ) );
225  data->len = data->max_len = cpu_to_le16 ( len );
226  }
227 
228  return ( payload + len );
229 }
230 
231 /**
232  * Append Unicode string data to NTLM message
233  *
234  * @v header Message header, or NULL to only calculate next payload
235  * @v data Data descriptor
236  * @v payload Data payload
237  * @v string String to append, or NULL
238  * @ret payload Next data payload
239  */
240 static void * ntlm_append_string ( struct ntlm_header *header,
241  struct ntlm_data *data, void *payload,
242  const char *string ) {
243  uint16_t *tmp = payload;
244  uint8_t c;
245 
246  /* Convert string to Unicode */
247  for ( tmp = payload ; ( string && ( c = *(string++) ) ) ; tmp++ ) {
248  if ( header )
249  *tmp = cpu_to_le16 ( c );
250  }
251 
252  /* Append string data */
253  return ntlm_append ( header, data, payload,
254  ( ( ( void * ) tmp ) - payload ) );
255 }
256 
257 /**
258  * Construct NTLM Authenticate message
259  *
260  * @v info Challenge information
261  * @v domain Domain name, or NULL
262  * @v username User name, or NULL
263  * @v workstation Workstation name, or NULL
264  * @v lm LAN Manager response
265  * @v nt NT response
266  * @v auth Message to fill in, or NULL to only calculate length
267  * @ret len Length of message
268  */
269 size_t ntlm_authenticate ( struct ntlm_challenge_info *info, const char *domain,
270  const char *username, const char *workstation,
271  struct ntlm_lm_response *lm,
272  struct ntlm_nt_response *nt,
273  struct ntlm_authenticate *auth ) {
274  void *tmp;
275  size_t nt_len;
276  size_t len;
277 
278  /* Construct response header */
279  if ( auth ) {
280  memset ( auth, 0, sizeof ( *auth ) );
282  sizeof ( auth->header.magic ) );
284  auth->flags = ntlm_negotiate.flags;
285  }
286  tmp = ( ( ( void * ) auth ) + sizeof ( *auth ) );
287 
288  /* Construct LAN Manager response */
289  if ( auth )
290  memcpy ( tmp, lm, sizeof ( *lm ) );
291  tmp = ntlm_append ( &auth->header, &auth->lm, tmp, sizeof ( *lm ) );
292 
293  /* Construct NT response */
294  nt_len = ( sizeof ( *nt ) + info->len + sizeof ( nt->zero ) );
295  if ( auth ) {
296  memcpy ( tmp, nt, sizeof ( *nt ) );
297  memcpy ( ( tmp + sizeof ( *nt ) ), info->target, info->len );
298  memset ( ( tmp + sizeof ( *nt ) + info->len ), 0,
299  sizeof ( nt->zero ) );
300  }
301  tmp = ntlm_append ( &auth->header, &auth->nt, tmp, nt_len );
302 
303  /* Populate domain, user, and workstation names */
304  tmp = ntlm_append_string ( &auth->header, &auth->domain, tmp, domain );
305  tmp = ntlm_append_string ( &auth->header, &auth->user, tmp, username );
306  tmp = ntlm_append_string ( &auth->header, &auth->workstation, tmp,
307  workstation );
308 
309  /* Calculate length */
310  len = ( tmp - ( ( void * ) auth ) );
311  if ( auth ) {
312  DBGC ( auth, "NTLM authenticate message:\n" );
313  DBGC_HDA ( auth, 0, auth, len );
314  }
315 
316  return len;
317 }
318 
319 /**
320  * Calculate NTLM Authenticate message length
321  *
322  * @v info Challenge information
323  * @v domain Domain name, or NULL
324  * @v username User name, or NULL
325  * @v workstation Workstation name, or NULL
326  * @ret len Length of Authenticate message
327  */
329  const char *domain, const char *username,
330  const char *workstation ) {
331 
332  return ntlm_authenticate ( info, domain, username, workstation,
333  NULL, NULL, NULL );
334 }
#define MD4_DIGEST_SIZE
MD4 digest size.
Definition: md4.h:69
#define EINVAL
Invalid argument.
Definition: errno.h:428
static wchar_t wc
Definition: wchar.h:22
static void digest_update(struct digest_algorithm *digest, void *ctx, const void *data, size_t len)
Definition: crypto.h:177
unsigned short uint16_t
Definition: stdint.h:11
A variable-length data descriptor.
Definition: ntlm.h:74
uint8_t magic[8]
Magic signature.
Definition: ntlm.h:19
u32 info
Definition: ar9003_mac.h:67
struct ntlm_data lm
LAN Manager response.
Definition: ntlm.h:120
#define le32_to_cpu(value)
Definition: byteswap.h:113
Error codes.
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
void hmac_final(struct digest_algorithm *digest, void *digest_ctx, void *key, size_t *key_len, void *hmac)
Finalise HMAC.
Definition: hmac.c:115
static void digest_final(struct digest_algorithm *digest, void *ctx, void *out)
Definition: crypto.h:182
void ntlm_response(struct ntlm_challenge_info *info, struct ntlm_key *key, struct ntlm_nonce *nonce, struct ntlm_lm_response *lm, struct ntlm_nt_response *nt)
Construct NTLM responses.
Definition: ntlm.c:168
#define DBGC(...)
Definition: compiler.h:505
Negotiate NTLM key.
Definition: ntlm.h:46
static __always_inline void off_t int c
Definition: efi_uaccess.h:87
#define offsetof(type, field)
Get offset of a field within a structure.
Definition: stddef.h:24
struct golan_eq_context ctx
Definition: CIB_PRM.h:28
Character types.
struct md4_digest digest
Digest of data already processed.
Definition: md4.h:12
struct ntlm_header header
Message header.
Definition: ntlm.h:118
uint16_t len
Length (in bytes)
Definition: ntlm.h:76
Authenticate message.
Definition: ntlm.h:34
size_t ntlm_authenticate(struct ntlm_challenge_info *info, const char *domain, const char *username, const char *workstation, struct ntlm_lm_response *lm, struct ntlm_nt_response *nt, struct ntlm_authenticate *auth)
Construct NTLM Authenticate message.
Definition: ntlm.c:269
static int toupper(int character)
Convert character to upper case.
Definition: ctype.h:109
void * memcpy(void *dest, const void *src, size_t len) __nonnull
struct ntlm_data nt
NT response.
Definition: ntlm.h:16
u32 version
Version number.
Definition: ath9k_hw.c:1983
Negotiate always sign.
Definition: ntlm.h:44
Keyed-Hashing for Message Authentication.
#define DBGC_HDA(...)
Definition: compiler.h:506
An Authenticate message.
Definition: ntlm.h:116
#define NTLM_MAGIC
Magic signature.
Definition: ntlm.h:25
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
An NTLM verification key.
Definition: ntlm.h:175
#define cpu_to_le32(value)
Definition: byteswap.h:107
uint32_t flags
Negotiation flags.
Definition: ntlm.h:92
static void digest_init(struct digest_algorithm *digest, void *ctx)
Definition: crypto.h:172
struct ntlm_data workstation
Workstation name.
Definition: ntlm.h:128
A message header.
Definition: ntlm.h:17
static void * ntlm_append_string(struct ntlm_header *header, struct ntlm_data *data, void *payload, const char *string)
Append Unicode string data to NTLM message.
Definition: ntlm.c:240
uint8_t * tmp
Definition: entropy.h:156
#define MD5_CTX_SIZE
MD5 context size.
Definition: md5.h:66
long int random(void)
Generate a pseudo-random number between 0 and 2147483647L or 2147483562?
Definition: random.c:30
struct ntlm_data domain
Domain name.
Definition: ntlm.h:124
struct ntlm_data lm
LAN Manager response.
Definition: ntlm.h:14
static void * ntlm_append(struct ntlm_header *header, struct ntlm_data *data, void *payload, size_t len)
Append data to NTLM message.
Definition: ntlm.c:219
unsigned char uint8_t
Definition: stdint.h:10
struct ntlm_header header
Message header.
Definition: ntlm.h:90
A nonce.
Definition: ntlm.h:68
Negotiate extended security.
Definition: ntlm.h:42
#define le16_to_cpu(value)
Definition: byteswap.h:112
struct ntlm_data nt
NT response.
Definition: ntlm.h:122
uint32_t flags
Negotiation flags.
Definition: ntlm.h:132
struct ntlm_data user
User name.
Definition: ntlm.h:126
#define MD4_CTX_SIZE
MD4 context size.
Definition: md4.h:66
struct ntlm_data info
Target information.
Definition: ntlm.h:112
Negotiate message type.
Definition: ntlm.h:30
uint32_t len
Length.
Definition: ena.h:14
A Challenge message.
Definition: ntlm.h:100
void hmac_init(struct digest_algorithm *digest, void *digest_ctx, void *key, size_t *key_len)
Initialise HMAC.
Definition: hmac.c:80
size_t ntlm_authenticate_len(struct ntlm_challenge_info *info, const char *domain, const char *username, const char *workstation)
Calculate NTLM Authenticate message length.
Definition: ntlm.c:328
#define NTLM_VERSION_NTLMV2
NTLM version.
Definition: ntlm.h:162
uint32_t offset
Offset from start of message header.
Definition: ntlm.h:84
uint32_t type
Message type.
Definition: ntlm.h:21
struct ena_aq_header header
Header.
Definition: ena.h:12
#define cpu_to_le16(value)
Definition: byteswap.h:106
A message digest algorithm.
Definition: crypto.h:16
int ntlm_challenge(struct ntlm_challenge *challenge, size_t len, struct ntlm_challenge_info *info)
Parse NTLM Challenge.
Definition: ntlm.c:68
uint8_t raw[8]
Raw bytes.
Definition: ntlm.h:70
struct ntlm_data workstation
Workstation name.
Definition: ntlm.h:18
void ntlm_key(const char *domain, const char *username, const char *password, struct ntlm_key *key)
Calculate NTLM verification key.
Definition: ntlm.c:114
NT LAN Manager (NTLM) authentication.
struct digest_algorithm md4_algorithm
MD4 algorithm.
Definition: md4.c:262
struct ntlm_nonce nonce
Server nonce.
Definition: ntlm.h:108
struct arbelprm_port_state_change_st data
Message.
Definition: arbel.h:12
NTLM challenge information.
Definition: ntlm.h:165
static void hmac_update(struct digest_algorithm *digest, void *digest_ctx, const void *data, size_t len)
Update HMAC.
Definition: hmac.h:21
MD4 algorithm.
An NT response.
Definition: ntlm.h:144
MD5 algorithm.
Request target name and information.
Definition: ntlm.h:48
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
A LAN Manager response.
Definition: ntlm.h:136
String functions.
Negotiate Unicode character encoding.
Definition: ntlm.h:50
struct ntlm_nonce nonce
Server nonce.
Definition: ntlm.h:18
union @375 key
Sense key.
Definition: scsi.h:18
struct ntlm_data domain
Domain name.
Definition: ntlm.h:16
struct digest_algorithm md5_algorithm
MD5 algorithm.
Definition: md5.c:287
void * memset(void *dest, int character, size_t len) __nonnull
A Negotiate message.
Definition: ntlm.h:88