(let ((request-ident (concat "NTLMSSP" (make-string 1 0)))
(request-msgType (concat (make-string 1 1) (make-string 3 0)))
;0x01 0x00 0x00 0x00
- (request-flags (concat (make-string 1 7) (make-string 1 178)
+ (request-flags (concat (make-string 1 7) (make-string 1 130)
(make-string 1 8) (make-string 1 0)))
- ;0x07 0xb2 0x08 0x00
+ ;0x07 0x82 0x08 0x00
lu ld off-d off-u)
- (when (string-match "@" user)
+ (when (and user (string-match "@" user))
(unless domain
(setq domain (substring user (1+ (match-beginning 0)))))
(setq user (substring user 0 (match-beginning 0))))
+ (when (and (stringp domain) (> (length domain) 0))
+ ;; set "negotiate domain supplied" bit
+ (aset request-flags 1 (logior (aref request-flags 1) ?\x10)))
;; set fields offsets within the request struct
(setq lu (length user))
(setq ld (length domain))
;;(ident (substring rchallenge 0 8)) ;ident, 8 bytes
;;(msgType (substring rchallenge 8 12)) ;msgType, 4 bytes
(uDomain (substring rchallenge 12 20)) ;uDomain, 8 bytes
+ ;; match default setting in `ntlm-build-auth-request'
+ (request-flags (concat (make-string 1 7) (make-string 1 130)
+ (make-string 1 8) (make-string 1 0)))
+ ;0x07 0x82 0x08 0x00
(flags (substring rchallenge 20 24)) ;flags, 4 bytes
(challengeData (substring rchallenge 24 32)) ;challengeData, 8 bytes
uDomain-len uDomain-offs
lmRespData ;lmRespData, 24 bytes
ntRespData ;ntRespData, variable length
domain ;ascii domain string
- lu ld ln off-lm off-nt off-d off-u off-w off-s)
+ workstation ;ascii workstation string
+ ll ln lu ld lw off-lm off-nt off-u off-d off-w)
;; extract domain string from challenge string
(setq uDomain-len (md4-unpack-int16 (substring uDomain 0 2)))
(setq uDomain-offs (md4-unpack-int32 (substring uDomain 4 8)))
- (setq domain
- (ntlm-unicode2ascii (substring challenge
- (cdr uDomain-offs)
- (+ (cdr uDomain-offs) uDomain-len))
- (/ uDomain-len 2)))
+ ;; match Mozilla behavior, which is to send an empty domain string
+ (setq domain "")
+ ;; match Mozilla behavior, which is to send "WORKSTATION"
+ (setq workstation "WORKSTATION")
;; overwrite domain in case user is given in <user>@<domain> format
(when (string-match "@" user)
(setq domain (substring user (1+ (match-beginning 0))))
(setq user (substring user 0 (match-beginning 0))))
+ (when (and (stringp domain) (> (length domain) 0))
+ ;; set "negotiate domain supplied" bit, since presumably domain
+ ;; was also set in `ntlm-build-auth-request'
+ (aset request-flags 1 (logior (aref request-flags 1) ?\x10)))
+ ;; match Mozilla behavior, which is to send the logical and of the
+ ;; type 1 and type 2 flags
+ (dotimes (index 4)
+ (aset flags index (logand (aref flags index)
+ (aref request-flags index))))
(unless (and (integerp ntlm-compatibility-level)
(>= ntlm-compatibility-level 0)
(ntlm-compute-timestamp) ; timestamp
nonce ; client nonce
(make-string 4 0) ; unknown
- targetInfo ; target info
- (make-string 4 0))) ; unknown
+ targetInfo)) ; target info
;; for reference: LMv2 interim calculation
- ;; (lm-interim (hmac-md5 (concat challengeData nonce)
- ;; ntlmv2-hash))
+ (lm-interim (hmac-md5 (concat challengeData nonce)
+ ntlmv2-hash))
(nt-interim (hmac-md5 (concat challengeData blob)
ntlmv2-hash)))
;; for reference: LMv2 field, but match other clients that
;; send all zeros
- ;; (setq lmRespData (concat lm-interim nonce))
- (setq lmRespData (make-string 24 0))
+ (setq lmRespData (concat lm-interim nonce))
(setq ntRespData (concat nt-interim blob))))
;; compatibility level is 2, 1 or 0
;; level 2 should be treated specially but it's not clear how,
(ntlm-smb-owf-encrypt (cadr password-hashes) challengeData))))
;; get offsets to fields to pack the response struct in a string
+ (setq ll (length lmRespData))
+ (setq ln (length ntRespData))
(setq lu (length user))
(setq ld (length domain))
- (setq ln (length ntRespData))
- (setq off-lm 64) ;offset to string 'lmResponse
- (setq off-nt (+ 64 24)) ;offset to string 'ntResponse
- (setq off-d (+ 64 24 ln)) ;offset to string 'uDomain
- (setq off-u (+ 64 24 ln (* 2 ld))) ;offset to string 'uUser
- (setq off-w (+ 64 24 ln (* 2 (+ ld lu)))) ;offset to string 'uWks
- (setq off-s (+ 64 24 ln (* 2 (+ ld lu lu)))) ;offset to string 'sessionKey
+ (setq lw (length workstation))
+ (setq off-u 64) ;offset to string 'uUser
+ (setq off-d (+ off-u (* 2 lu))) ;offset to string 'uDomain
+ (setq off-w (+ off-d (* 2 ld))) ;offset to string 'uWks
+ (setq off-lm (+ off-w (* 2 lw))) ;offset to string 'lmResponse
+ (setq off-nt (+ off-lm ll)) ;offset to string 'ntResponse
;; pack the response struct in a string
(concat "NTLMSSP\0" ;response ident field, 8 bytes
(md4-pack-int32 '(0 . 3)) ;response msgType field, 4 bytes
;; lmResponse field, 8 bytes
;;AddBytes(response,lmResponse,lmRespData,24);
- (md4-pack-int16 24) ;len field
- (md4-pack-int16 24) ;maxlen field
+ (md4-pack-int16 ll) ;len field
+ (md4-pack-int16 ll) ;maxlen field
(md4-pack-int32 (cons 0 off-lm)) ;field offset
;; ntResponse field, 8 bytes
;;AddBytes(response, uDomain, udomain, 2*ld);
(md4-pack-int16 (* 2 ld)) ;len field
(md4-pack-int16 (* 2 ld)) ;maxlen field
- (md4-pack-int32 (cons 0 off-d)) ;field offset
+ ;; match Mozilla behavior, which is to hard-code the
+ ;; domain offset to 64
+ (md4-pack-int32 (cons 0 64)) ;field offset
;; uUser field, 8 bytes
;;AddUnicodeString(response,uUser,u);
;; uWks field, 8 bytes
;;AddUnicodeString(response,uWks,u);
- (md4-pack-int16 (* 2 lu)) ;len field
- (md4-pack-int16 (* 2 lu)) ;maxlen field
+ (md4-pack-int16 (* 2 lw)) ;len field
+ (md4-pack-int16 (* 2 lw)) ;maxlen field
(md4-pack-int32 (cons 0 off-w)) ;field offset
- ;; sessionKey field, 8 bytes
+ ;; sessionKey field, blank, 8 bytes
;;AddString(response,sessionKey,NULL);
(md4-pack-int16 0) ;len field
(md4-pack-int16 0) ;maxlen field
- (md4-pack-int32 (cons 0 (- off-s off-lm))) ;field offset
+ (md4-pack-int32 (cons 0 0)) ;field offset
;; flags field, 4 bytes
flags ;
;; buffer field
+ (ntlm-ascii2unicode user lu) ;Unicode user, 2*lu bytes
+ (ntlm-ascii2unicode domain ld) ;Unicode domain, 2*ld bytes
+ (ntlm-ascii2unicode workstation lw) ;Unicode workstation, 2*lw bytes
lmRespData ;lmResponse, 24 bytes
- ntRespData ;ntResponse, 24 bytes
- (ntlm-ascii2unicode domain ;Unicode domain string, 2*ld bytes
- (length domain)) ;
- (ntlm-ascii2unicode user ;Unicode user string, 2*lu bytes
- (length user)) ;
- (ntlm-ascii2unicode user ;Unicode user string, 2*lu bytes
- (length user)) ;
+ ntRespData ;ntResponse, ln bytes
)))
(defun ntlm-get-password-hashes (password)