1 /*-
2 * Copyright (c) 1998-1999 Whistle Communications, Inc.
3 * Copyright (c) 1998-1999 Archie Cobbs <archie@freebsd.org>
4 * Copyright (c) 2003 Michael Bretterklieber
5 * Copyright (c) 2017-2019 Björn Esser <besser82@fedoraproject.org>
6 * Copyright (c) 2017-2019 Zack Weinberg <zackw at panix.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include "crypt-port.h"
32
33 #if INCLUDE_nt
34
35 #include "alg-md4.h"
36
37 #include <errno.h>
38 #include <stdlib.h>
39
40 #define MD4_HASHLEN 16
41
42 typedef struct
43 {
44 MD4_CTX ctx;
45 uint8_t unipw[CRYPT_MAX_PASSPHRASE_SIZE * 2];
46 unsigned char hash[MD4_HASHLEN];
47 } crypt_nt_internal_t;
48
49 static_assert (sizeof (crypt_nt_internal_t) <= ALG_SPECIFIC_SIZE,
50 "ALG_SPECIFIC_SIZE is too small for NTHASH.");
51
52 /*
53 * NT HASH = md4(str2unicode(phrase))
54 */
55
56 void
57 crypt_nt_rn (const char *phrase, size_t phr_size,
58 const char *setting, size_t ARG_UNUSED (set_size),
59 uint8_t *output, size_t out_size,
60 void *scratch, size_t scr_size)
61 {
62 static const char *magic = "$3$";
63 static const uint8_t *hexconvtab = (const uint8_t*) "0123456789abcdef";
64
65 if ((out_size < strlen (magic) + MD4_HASHLEN * 2 + 1) ||
66 (scr_size < sizeof (crypt_nt_internal_t)))
67 {
68 errno = ERANGE;
69 return;
70 }
71
72 if (strncmp (setting, magic, strlen (magic)))
73 {
74 errno = EINVAL;
75 return;
76 }
77
78 crypt_nt_internal_t *intbuf = scratch;
79
80 /* Convert the input to UCS-2LE, blindly assuming that it was
81 IANA ISO_8859-1:1987 to begin with (i.e. 0x00 .. 0xFF
82 encode U+0000 .. U+FFFF; technically this is a superset
83 of the original ISO 8859.1). Note that this does not
84 U+0000-terminate intbuf->unipw. */
85 for (size_t i = 0; i < phr_size; i++)
86 {
87 intbuf->unipw[2*i ] = (uint8_t)phrase[i];
88 intbuf->unipw[2*i + 1] = 0x00;
89 }
90
91 /* Compute MD4 of Unicode password. */
92 MD4_Init (&intbuf->ctx);
93 MD4_Update (&intbuf->ctx, intbuf->unipw, phr_size * 2);
94 MD4_Final (intbuf->hash, &intbuf->ctx);
95
96 /* Write the computed hash to the output buffer. */
97 output += strcpy_or_abort (output, out_size, magic);
98 *output++ = '$';
99 for (size_t i = 0; i < MD4_HASHLEN; i++)
100 {
101 *output++ = hexconvtab[intbuf->hash[i] >> 4];
102 *output++ = hexconvtab[intbuf->hash[i] & 0xf];
103 }
104 *output = '\0';
105 }
106
107 /* This function simply returns the magic string '$3$',
108 so it can be used as SETTING for the crypt function. */
109 void
110 gensalt_nt_rn (unsigned long count,
111 ARG_UNUSED(const uint8_t *rbytes),
112 ARG_UNUSED(size_t nrbytes),
113 uint8_t *output,
114 size_t o_size)
115 {
116 const char *prefix = "$3$";
117
118 /* Minimal O_SIZE to store the prefix. */
119 if (o_size < strlen (prefix) + 1)
120 {
121 errno = ERANGE;
122 return;
123 }
124
125 if (count != 0)
126 {
127 errno = EINVAL;
128 return;
129 }
130
131 strcpy_or_abort (output, o_size, prefix);
132 }
133
134 #endif