1 /*
2  * Copyright (c) 2000
3  *      Traakan, Inc., Los Altos, CA
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * Project:  NDMJOB
31  * Ident:    $Id: $
32  *
33  * Description:
34  *
35  * MD5 authentication support
36  ****************************************************************
37  * Both sides share a secret in the form of a clear-text
38  * password. One side generates a challenge (64-bytes)
39  * and conveys it to the other. The other side then
40  * uses the challenge, the clear-text password, and
41  * the NDMP rules for MD5, and generates a digest.
42  * The digest is returned as proof that both sides
43  * share the same secret clear-text password.
44  *
45  * The NDMP rules for MD5 are implemented in ndmmd5_digest().
46  * It amounts to positioning the clear-text password and challenge
47  * into a "message" buffer, then applying the MD5 algorithm.
48  *
49  * ndmmd5_generate_challenge() generates a challenge[]
50  * using conventional random number routines.
51  *
52  * ndmmd5_ok_digest() takes a locally known challenge[]
53  * and clear-text password, a remotely generated
54  * digest[], and determines if everything is correct.
55  *
56  * Using MD5 prevents clear-text passwords from being conveyed
57  * over the network. However, it compels both sides to maintain
58  * clear-text passwords in a secure fashion, which is difficult
59  * to say the least. Because the NDMP MD5 rules must be followed
60  * to digest() the password, it's impractical to consider
61  * an external authentication authority.
62  *
63  * Credits to Rajiv of NetApp for helping with MD5 stuff.
64  */
65 
66 
67 #include "ndmlib.h"
68 #include "md5.h"
69 
70 
ndmmd5_generate_challenge(char challenge[NDMP_MD5_CHALLENGE_LENGTH])71 int ndmmd5_generate_challenge(char challenge[NDMP_MD5_CHALLENGE_LENGTH])
72 {
73   int i;
74 
75   NDMOS_MACRO_SRAND();
76 
77   for (i = 0; i < NDMP_MD5_CHALLENGE_LENGTH; i++) {
78     challenge[i] = NDMOS_MACRO_RAND() >> (i & 7);
79   }
80 
81   return 0;
82 }
83 
84 
ndmmd5_ok_digest(char challenge[NDMP_MD5_CHALLENGE_LENGTH],char * clear_text_password,char digest[NDMP_MD5_DIGEST_LENGTH])85 int ndmmd5_ok_digest(char challenge[NDMP_MD5_CHALLENGE_LENGTH],
86                      char* clear_text_password,
87                      char digest[NDMP_MD5_DIGEST_LENGTH])
88 {
89   char my_digest[16];
90   int i;
91 
92   ndmmd5_digest(challenge, clear_text_password, my_digest);
93 
94   for (i = 0; i < NDMP_MD5_DIGEST_LENGTH; i++)
95     if (digest[i] != my_digest[i]) return 0; /* Invalid */
96 
97   return 1; /* OK */
98 }
99 
100 
ndmmd5_digest(char challenge[NDMP_MD5_CHALLENGE_LENGTH],char * clear_text_password,char digest[NDMP_MD5_DIGEST_LENGTH])101 int ndmmd5_digest(char challenge[NDMP_MD5_CHALLENGE_LENGTH],
102                   char* clear_text_password,
103                   char digest[NDMP_MD5_DIGEST_LENGTH])
104 {
105   int pwlength = strlen(clear_text_password);
106   struct MD5Context mdContext;
107   unsigned char message[128];
108 
109   /*
110    * The spec describes the construction of the 128 byte
111    * "message" (probably MD5-speak). It is described as:
112    *
113    *    PASSWORD PADDING CHALLENGE PADDING PASSWORD
114    *
115    * Each PADDING is defined as zeros of length 64 minus pwlen.
116    *
117    * A pwlen of over 32 would result in not all fields
118    * fitting. This begs a question of the order elements
119    * are inserted into the message[]. You get a different
120    * message[] if you insert the PASSWORD(s) before
121    * the CHALLENGE than you get the other way around.
122    *
123    * A pwlen of over 64 would result in PADDING of negative
124    * length, which could cause crash boom bang.
125    *
126    * The resolution of this vaguery implemented here is to
127    * only use the first 32 bytes of the password. All
128    * fields fit. Order dependencies are avoided.
129    *
130    * Final resolution is pending.
131    */
132   if (pwlength > 32) pwlength = 32;
133 
134   /*
135    * Compose the 128-byte buffer according to NDMP rules
136    */
137   NDMOS_API_BZERO(message, sizeof message);
138   NDMOS_API_BCOPY(clear_text_password, &message[0], pwlength);
139   NDMOS_API_BCOPY(clear_text_password, &message[128 - pwlength], pwlength);
140   NDMOS_API_BCOPY(challenge, &message[64 - pwlength], 64);
141 
142   /*
143    * Grind it up, ala MD5
144    */
145   MD5Init(&mdContext);
146   MD5Update(&mdContext, message, 128);
147   MD5Final((unsigned char*)digest, &mdContext);
148 
149   /*
150    * ding! done
151    */
152   return 0;
153 }
154