1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2019 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * This is a Bacula plugin for backup/restore Docker using native tools.
21  *
22  * Author: Radosław Korzeniewski, MMXIX
23  * radoslaw@korzeniewski.net, radekk@inteos.pl
24  * Inteos Sp. z o.o. http://www.inteos.pl/
25  */
26 
27 #include "dkid.h"
28 
29 /*
30  * DKID class constructor, does default initialization.
31  */
DKID()32 DKID::DKID()
33 {
34    bmemzero(Digest, DKIDDIGESTSIZE + 1);
35    ShortD = DKIDInvalid;
36    shortonly = false;
37 };
38 
39 /*
40  * DKID class constructor, does parm initialization.
41  */
DKID(const char * data)42 DKID::DKID(const char* data)
43 {
44    init(data);
45 };
46 
47 /*
48  * DKID class constructor, does parm initialization.
49  */
DKID(POOL_MEM & data)50 DKID::DKID(POOL_MEM& data)
51 {
52    init(data.c_str());
53 };
54 
55 /*
56  * DKID initialization from string.
57  *    as the usable area of short sha256 version used in Docker is 6bytes/48bits
58  *    and we are using a 64bit (signed) integer then we have a plenty of space to mark
59  *    invalid sha256 conversion with a negative ShortD value.
60  */
init(const char * data)61 void DKID::init(const char* data)
62 {
63    int len;
64    int a;
65    unsigned char c;
66    bool valid = true;
67    char *dig = (char*)data;
68 
69    if (dig != NULL){
70       /* check for sha256: prefix*/
71       if (strstr(dig, "sha256:") == dig){
72          dig += 7;
73       }
74       len = strlen(dig);
75       /* check for invalid input data */
76       for (a = 0; a < (len > DKIDDIGESTShortSIZE ? DKIDDIGESTShortSIZE : len); a++){
77          // we are checking for ASCII codes, a subset of UTF-8 for short digest only
78          c = (unsigned char)dig[a];
79          if (c > 'f' || (c > '9' && c < 'A') || (c > 'F' && c < 'a')){
80             valid = false;
81             break;
82          }
83       }
84       if (valid){
85          if (len > DKIDDIGESTShortSIZE){
86             /* initialize from full data */
87             memcpy(Digest, dig, DKIDDIGESTSIZE);
88             Digest[DKIDDIGESTSIZE] = 0;
89             shortonly = false;
90          } else {
91             /* handle short data */
92             memcpy(Digest, dig, len);
93             memcpy(Digest + len, "(...)\0", 6);
94             shortonly = true;
95          }
96          memcpy(DigestShort, dig, DKIDDIGESTShortSIZE);
97          DigestShort[DKIDDIGESTShortSIZE] = 0;
98          ShortD = strtol(DigestShort, NULL, 16);
99       } else {
100          ShortD = DKIDInvalid;
101          shortonly = false;
102       }
103    }
104 };
105 
106 /*
107  * Basic assignment operator overloading for string.
108  *
109  * in:
110  *    data - the null terminated string where up to 64 chars will be used
111  * out:
112  *    reinitialized DKID class
113  */
114 DKID& DKID::operator= (char* data)
115 {
116    init(data);
117    return *this;
118 };
119 
120 /*
121  * Basic assignment operator overloading for POOL_MEM class.
122  *
123  * in:
124  *    data - a reference to POOL_MEM class instance which is used as a source
125  *           of null terminated string for initialization
126  * out:
127  *    reinitialized DKID class
128  */
129 DKID& DKID::operator =(POOL_MEM &data)
130 {
131    init(data.c_str());
132    return *this;
133 }
134 
135 /*
136  * Basic assignment operator overloading for DKID class.
137  *
138  * in:
139  *    other - a reference to another DKID class instance which will be used for
140  *            assignment
141  * out:
142  *    reinitialized DKID class
143  */
144 DKID& DKID::operator =(DKID &other)
145 {
146    memcpy(Digest, other.Digest, DKIDDIGESTSIZE);
147    memcpy(DigestShort, other.DigestShort, DKIDDIGESTShortSIZE);
148    Digest[DKIDDIGESTSIZE] = 0;
149    DigestShort[DKIDDIGESTShortSIZE] = 0;
150    ShortD = other.ShortD;
151    shortonly = other.shortonly;
152    return *this;
153 }
154 
155 /*
156  * Equal to operator overloading for DKID class.
157  *
158  * in:
159  *    other - a reference to another DKID class instance which will be used for
160  *            comparison
161  * out:
162  *    true - if both ShortD are the same
163  *    false - if ShortD variables differ or any DKID is invalid
164  */
165 bool DKID::operator ==(DKID &other)
166 {
167    if (ShortD >= 0 && other.ShortD >= 0 && ShortD == other.ShortD &&
168          (shortonly || other.shortonly || bstrcmp(Digest, other.Digest))){
169       return true;
170    }
171    return false;
172 }
173 
174 /*
175  * Not-Equal to operator overloading for DKID class.
176  *
177  * in:
178  *    other - a reference to another DKID class instance which will be used for
179  *            comparison
180  * out:
181  *    true - if ShortD variables differ and none of them are invalid
182  *    false - if both ShortD are the same or any DKID is invalid
183  */
184 bool DKID::operator !=(DKID &other)
185 {
186    if (ShortD >= 0 && other.ShortD >= 0 && ShortD != other.ShortD){
187       return true;
188    }
189    if (!shortonly && !other.shortonly && !bstrcmp(Digest, other.Digest)){
190       return true;
191    }
192    return false;
193 }
194 
195 #ifndef TEST_PROGRAM
196 #define TEST_PROGRAM_A
197 #endif
198 
199 #ifdef TEST_PROGRAM
200 #include "unittests.h"
201 
dump()202 void DKID::dump()
203 {
204    printf ("%p::ShortD: %ld\n", this, ShortD);
205    printf ("%p::Digest: %s\n", this, Digest);
206    printf ("%p::shortonly: %s\n", this, shortonly?"true":"false");
207    printf ("%p::DigestShort: %s\n", this, DigestShort);
208 };
209 
210 const char *dig1     =  "66f45d8601bae26a6b2ffeb46922318534d3b3905377b3a224693bd78601cb3b";
211 const char *sdig1    =  "66f45d8601ba";
212 const int64_t vdig1 = 0x66f45d8601ba;
213 const char *dig2     =  "B546087C43F75A2C1484B4AEE0737499AA69A09067B04237907FCCD4BDE938C7";
214 const char *sdig2    =  "B546087C43F7";
215 const int64_t vdig2 = 0xb546087c43f7;
216 const char *sdig3    =  "0f601bcb1ef5";
217 const int64_t vdig3 = 0x0f601bcb1ef5;
218 const char *sdig4    =  "00571da76d";
219 const int64_t vdig4 = 0x00571da76d;
220 const char *dig5     = "sha256:daabf4372f900cb1ad0db17d26abf3acce55224275d1850f02459180e4dacf1d";
221 const char *tdig5    = "daabf4372f900cb1ad0db17d26abf3acce55224275d1850f02459180e4dacf1d";
222 const char *sdig5    = "daabf4372f90";
223 const int64_t vdig5  = 0xdaabf4372f90;
224 const char *sinv1 = "Invalid initialization string";
225 const char *sinv2 = "brave_edison";
226 const char *sinv3 = "0xDEADBEEF";
227 const char *sinv4 = "c0a478d317195b…";
228 const char *sinv5 = "a478d317195b…";
229 const char *sinv6 = "78d317195b…";
230 
main()231 int main()
232 {
233    Unittests dkid_test("dkid_test");
234    DKID *id;
235    DKID id2(dig2);
236    char *p;
237    int64_t v;
238    POOL_MEM m(PM_FNAME);
239 
240    Pmsg0(0, "Initialize tests ...\n");
241 
242    id = New(DKID);
243    ok(id && id->id() == DKIDInvalid, "Check default initialization short");
244    ok(id && strlen(id->digest()) == 0, "Check default initialization full");
245    ok(id && strlen(id->digest_short()) == 0, "Check short default initialization full");
246    delete(id);
247 
248    id = New(DKID(dig1));
249    ok(id && id->id() == vdig1, "Check param initialization short");
250    ok(id && bstrcmp(id->digest(), dig1), "Check param initialization full");
251    ok(id && bstrcmp(id->digest_short(), sdig1), "Check short param initialization");
252    delete(id);
253 
254    id = New(DKID(dig2));
255    ok(id && id->id() == vdig2, "Check param initialization short upper");
256    ok(id && bstrcmp(id->digest(), dig2), "Check param initialization full upper");
257    ok(id && bstrcmp(id->digest_short(), sdig2), "Check short param initialization full upper");
258    delete(id);
259 
260    Mmsg(m, "%s", dig1);
261    id = New(DKID(m));
262    ok(id && id->id() == vdig1, "Check pool_mem initialization short");
263    ok(id && bstrcmp(id->digest(), dig1), "Check pool_mem initialization full");
264    ok(id && bstrcmp(id->digest_short(), sdig1), "Check short pool_mem initialization full");
265    delete(id);
266 
267    id = New(DKID(sdig3));
268    ok(id && id->id() == vdig3, "Check short digest initialization");
269    Mmsg(m, "%s(...)", sdig3);
270    ok(id && bstrcmp(id->digest(), m.c_str()), "Check short digest initialization full str");
271    ok(id && bstrcmp(id->digest_short(), sdig3), "Check short for short digest initialization");
272    delete(id);
273 
274    id = New(DKID(sdig4));
275    ok(id && id->id() == vdig4, "Check shorter digest initialization");
276    Mmsg(m, "%s(...)", sdig4);
277    ok(id && bstrcmp(id->digest(), m.c_str()), "Check shorter digest initialization full str");
278    ok(id && bstrcmp(id->digest_short(), sdig4), "Check short for shorter digest initialization");
279    delete(id);
280 
281    id = New(DKID(dig5));
282    ok(id && id->id() == vdig5, "Check param initialization with sha256: prefix");
283    ok(id && bstrcmp(id->digest(), tdig5), "Check param initialization full with sha256: prefix");
284    ok(id && bstrcmp(id->digest_short(), sdig5), "Check short param initialization with sha256: prefix");
285    delete(id);
286 
287    Pmsg0(0, "Invalid initialization tests ...\n");
288 
289    id = New(DKID(sinv1));
290    ok(id && id->id() < 0, "Checking invalid digest string long");
291    delete(id);
292 
293    id = New(DKID(sinv2));
294    ok(id && id->id() < 0, "Checking invalid digest string short");
295    delete(id);
296 
297    id = New(DKID(sinv3));
298    ok(id && id->id() < 0, "Checking invalid digest string hex");
299    delete(id);
300 
301    id = New(DKID(sinv4));
302    ok(id && id->id() >= 0, "Checking digest string with ellipsis");
303    delete(id);
304 
305    id = New(DKID(sinv5));
306    ok(id && id->id() >= 0, "Checking digest string with ellipsis short");
307    delete(id);
308 
309    id = New(DKID(sinv6));
310    ok(id && id->id() < 0, "Checking invalid digest string with ellipsis short");
311    delete(id);
312 
313    Pmsg0(0, "Operators tests ...\n");
314 
315    id = New(DKID(dig1));
316    p = (char*)id;
317    ok(bstrcmp(p, dig1), "Checking operator char* ()");
318    v = *id;
319    ok(v == vdig1, "Checking operator int64_t ()");
320 
321    id2 = *id;
322    ok(id2.id() == vdig1, "Checking operator= (DKID&)");
323    ok(id2 == *id, "Checking operator== on the same");
324    nok(id2 != *id, "Checking operator!= on the same");
325 
326    *id = (char*)dig2;
327    ok(id->id() == vdig2, "Checking operator= (char*)");
328    nok(id2 == *id, "Checking operator== on different");
329    ok(id2 != *id, "Checking operator!= on different");
330 
331    *id = m;
332    ok(id2.id() == vdig1, "Checking operator= (POOL_MEM&)");
333 
334    id2 = (char*)dig2;
335    ok(id2.id() == vdig2, "Checking operator= (char*)");
336    delete(id);
337 
338    id = New(DKID(sinv1));
339    id2 = *id;
340    nok (id2 == *id, "Checking operator== on invalid digest");
341    nok (id2 != *id, "Checking operator!= on invalid digest");
342    delete(id);
343 
344    id = New(DKID(sdig1));
345    id2 = (char*)dig1;
346    ok (id2 == *id, "Checking operator== on full and short digest");
347    nok (id2 != *id, "Checking operator!= on full and short digest");
348    delete(id);
349 
350    return report();
351 };
352 
353 #endif   /* TEST_PROGRAM */