blob: 0f8b03fe95ad3ccdabc5a3752c069ae59f72bd32 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From 687106e4269c2e926e0452a88c2d72ac95779a95 Mon Sep 17 00:00:00 2001
2From: Andrew Walker <awalker@ixsystems.com>
3Date: Mon, 11 Apr 2022 09:13:09 -0400
4Subject: [PATCH 1/4] add handling for cases where ad_entry() returns NULL
5
6With recent CVE fixes, ad_enty() may now return NULL. This
7commit adds basic error handling for these cases and asserting
8where such a return is totally unexpected. In case of
9ad_getid() and ad_forcegetid(), return CNID_INVALID rather
10than 0 to clarify for future people investigating this that
11a 0 here is an indication of error.
12
13In case of new_ad_header(), the valid_data_len of the
14adouble data may still be zero. This causes subsequent
15ad_entry() calls within new_ad_header() to fail. As such,
16overwrite valid_data-Len with AD_DATASZ2 or AD_DATASZ_EA
17depending on how adouble data is stored on disk.
18
19Another side-effect of the fix is that ad_entry() for
20ADEID_DID on ea-backed adouble data will return NULL. In
21this case, add explicit check for the backend before
22attempting to get the DID.
23
24Signed-off-by: Andrew Walker <awalker@ixsystems.com>
25---
26 etc/afpd/directory.c | 15 +++-
27 etc/afpd/file.c | 25 +++++--
28 etc/afpd/volume.c | 7 +-
29 etc/cnid_dbd/cmd_dbd_scanvol.c | 9 ++-
30 libatalk/adouble/ad_attr.c | 130 +++++++++++++++++++++++++++------
31 libatalk/adouble/ad_conv.c | 4 +-
32 libatalk/adouble/ad_date.c | 17 ++++-
33 libatalk/adouble/ad_flush.c | 27 ++++++-
34 libatalk/adouble/ad_open.c | 39 ++++++----
35 9 files changed, 215 insertions(+), 58 deletions(-)
36
37--- a/etc/afpd/directory.c
38+++ b/etc/afpd/directory.c
39@@ -1426,6 +1426,7 @@ int getdirparams(const AFPObj *obj,
40 struct maccess ma;
41 struct adouble ad;
42 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
43+ char *ade = NULL;
44 int bit = 0, isad = 0;
45 uint32_t aint;
46 uint16_t ashort;
47@@ -1520,7 +1521,10 @@ int getdirparams(const AFPObj *obj,
48
49 case DIRPBIT_FINFO :
50 if ( isad ) {
51- memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
52+ ade = ad_entry(&ad, ADEID_FINDERI);
53+ AFP_ASSERT(ade != NULL);
54+
55+ memcpy( data, ade, 32 );
56 } else { /* no appledouble */
57 memset( data, 0, 32 );
58 /* dot files are by default visible */
59@@ -1744,6 +1748,7 @@ int setdirparams(struct vol *vol, struct
60 struct timeval tv;
61
62 char *upath;
63+ char *ade = NULL;
64 struct dir *dir;
65 int bit, isad = 0;
66 int cdate, bdate;
67@@ -1905,6 +1910,8 @@ int setdirparams(struct vol *vol, struct
68 fflags &= htons(~FINDERINFO_ISHARED);
69 memcpy(finder_buf + FINDERINFO_FRFLAGOFF, &fflags, sizeof(uint16_t));
70 /* #2802236 end */
71+ ade = ad_entry(&ad, ADEID_FINDERI);
72+ AFP_ASSERT(ade != NULL);
73
74 if ( dir->d_did == DIRDID_ROOT ) {
75 /*
76@@ -1915,10 +1922,10 @@ int setdirparams(struct vol *vol, struct
77 * behavior one sees when mounting above another mount
78 * point.
79 */
80- memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
81- memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
82+ memcpy( ade, finder_buf, 10 );
83+ memcpy( ade + 14, finder_buf + 14, 18 );
84 } else {
85- memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
86+ memcpy( ade, finder_buf, 32 );
87 }
88 }
89 break;
90--- a/etc/afpd/file.c
91+++ b/etc/afpd/file.c
92@@ -296,6 +296,7 @@ int getmetadata(const AFPObj *obj,
93 {
94 char *data, *l_nameoff = NULL, *upath;
95 char *utf_nameoff = NULL;
96+ char *ade = NULL;
97 int bit = 0;
98 uint32_t aint;
99 cnid_t id = 0;
100@@ -497,7 +498,10 @@ int getmetadata(const AFPObj *obj,
101 }
102 else {
103 if ( adp ) {
104- memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
105+ ade = ad_entry(adp, ADEID_FINDERI);
106+ AFP_ASSERT(ade != NULL);
107+
108+ memcpy(fdType, ade, 4);
109
110 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
111 achar = '\x04';
112@@ -576,8 +580,19 @@ int getmetadata(const AFPObj *obj,
113 10.3 clients freak out. */
114
115 aint = st->st_mode;
116- if (adp) {
117- memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
118+ /*
119+ * ad_open() does not initialize adouble header
120+ * for symlinks. Hence this should be skipped to
121+ * avoid AFP_ASSERT here. Decision was made to
122+ * not alter ad_open() behavior so that
123+ * improper ops on symlink adoubles will be
124+ * more visible (assert).
125+ */
126+ if (adp && (ad_meta_fileno(adp) != AD_SYMLINK)) {
127+ ade = ad_entry(adp, ADEID_FINDERI);
128+ AFP_ASSERT(ade != NULL);
129+
130+ memcpy(fdType, ade, 4);
131 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
132 aint |= S_IFLNK;
133 }
134@@ -839,6 +854,7 @@ int setfilparams(const AFPObj *obj, stru
135 struct extmap *em;
136 int bit, isad = 1, err = AFP_OK;
137 char *upath;
138+ char *ade = NULL;
139 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
140 uint16_t ashort, bshort, oshort;
141 uint32_t aint;
142@@ -989,7 +1005,7 @@ int setfilparams(const AFPObj *obj, stru
143 /* second try with adouble open
144 */
145 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
146- LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
147+ LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error: %s", strerror(errno));
148 /*
149 * For some things, we don't need an adouble header:
150 * - change of modification date
151@@ -1021,6 +1037,9 @@ int setfilparams(const AFPObj *obj, stru
152
153 switch( bit ) {
154 case FILPBIT_ATTR :
155+ if (isad == 0) {
156+ break;
157+ }
158 ad_getattr(adp, &bshort);
159 oshort = bshort;
160 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
161@@ -1034,15 +1053,26 @@ int setfilparams(const AFPObj *obj, stru
162 ad_setattr(adp, bshort);
163 break;
164 case FILPBIT_CDATE :
165+ if (isad == 0) {
166+ break;
167+ }
168 ad_setdate(adp, AD_DATE_CREATE, cdate);
169 break;
170 case FILPBIT_MDATE :
171 break;
172 case FILPBIT_BDATE :
173+ if (isad == 0) {
174+ break;
175+ }
176 ad_setdate(adp, AD_DATE_BACKUP, bdate);
177 break;
178 case FILPBIT_FINFO :
179- if (default_type( ad_entry( adp, ADEID_FINDERI ))
180+ if (isad == 0) {
181+ break;
182+ }
183+ ade = ad_entry(adp, ADEID_FINDERI);
184+ AFP_ASSERT(ade != NULL);
185+ if (default_type(ade)
186 && (
187 ((em = getextmap( path->m_name )) &&
188 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
189@@ -1053,7 +1083,7 @@ int setfilparams(const AFPObj *obj, stru
190 )) {
191 memcpy(finder_buf, ufinderi, 8 );
192 }
193- memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
194+ memcpy(ade, finder_buf, 32 );
195 break;
196 case FILPBIT_UNIXPR :
197 if (upriv_bit) {
198@@ -1061,9 +1091,15 @@ int setfilparams(const AFPObj *obj, stru
199 }
200 break;
201 case FILPBIT_PDINFO :
202+ if (isad == 0) {
203+ break;
204+ }
205+ ade = ad_entry(adp, ADEID_FINDERI);
206+ AFP_ASSERT(ade != NULL);
207+
208 if (obj->afp_version < 30) { /* else it's UTF8 name */
209- memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
210- memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
211+ memcpy(ade, fdType, 4 );
212+ memcpy(ade + 4, "pdos", 4 );
213 break;
214 }
215 /* fallthrough */
216--- a/etc/afpd/volume.c
217+++ b/etc/afpd/volume.c
218@@ -305,6 +305,7 @@ static int getvolparams(const AFPObj *ob
219 VolSpace xbfree, xbtotal; /* extended bytes */
220 char *data, *nameoff = NULL;
221 char *slash;
222+ char *ade = NULL;
223
224 LOG(log_debug, logtype_afpd, "getvolparams: Volume '%s'", vol->v_localname);
225
226@@ -328,8 +329,10 @@ static int getvolparams(const AFPObj *ob
227 slash = vol->v_path;
228 if (ad_getentryoff(&ad, ADEID_NAME)) {
229 ad_setentrylen( &ad, ADEID_NAME, strlen( slash ));
230- memcpy(ad_entry( &ad, ADEID_NAME ), slash,
231- ad_getentrylen( &ad, ADEID_NAME ));
232+ ade = ad_entry(&ad, ADEID_NAME);
233+ AFP_ASSERT(ade != NULL);
234+
235+ memcpy(ade, slash, ad_getentrylen( &ad, ADEID_NAME ));
236 }
237 vol_setdate(vol->v_vid, &ad, st->st_mtime);
238 ad_flush(&ad);
239--- a/etc/cnid_dbd/cmd_dbd_scanvol.c
240+++ b/etc/cnid_dbd/cmd_dbd_scanvol.c
241@@ -560,6 +560,7 @@ static int read_addir(void)
242 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok)
243 {
244 int adflags = ADFLAGS_HF;
245+ int err;
246 cnid_t db_cnid, ad_cnid;
247 struct adouble ad;
248
249@@ -602,7 +603,13 @@ static cnid_t check_cnid(const char *nam
250 cwdbuf, name, strerror(errno));
251 return CNID_INVALID;
252 }
253- ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
254+ err = ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
255+ if (err == -1) {
256+ dbd_log(LOGSTD, "Error setting new CNID, malformed adouble: '%s/%s'",
257+ cwdbuf, name);
258+ ad_close(&ad, ADFLAGS_HF);
259+ return CNID_INVALID;
260+ }
261 ad_flush(&ad);
262 ad_close(&ad, ADFLAGS_HF);
263 }
264--- a/libatalk/adouble/ad_attr.c
265+++ b/libatalk/adouble/ad_attr.c
266@@ -2,8 +2,10 @@
267 #include "config.h"
268 #endif /* HAVE_CONFIG_H */
269
270+#include <stdlib.h>
271 #include <string.h>
272 #include <arpa/inet.h>
273+#include <atalk/util.h>
274 #include <atalk/adouble.h>
275 #include <atalk/logger.h>
276
277@@ -22,10 +24,17 @@ int ad_getattr(const struct adouble *ad,
278 *attr = 0;
279
280 if (ad_getentryoff(ad, ADEID_AFPFILEI)) {
281- memcpy(attr, ad_entry(ad, ADEID_AFPFILEI) + AFPFILEIOFF_ATTR, 2);
282+ char *adp = NULL;
283+
284+ adp = ad_entry(ad, ADEID_AFPFILEI);
285+ AFP_ASSERT(adp != NULL);
286+ memcpy(attr, adp + AFPFILEIOFF_ATTR, 2);
287
288 /* Now get opaque flags from FinderInfo */
289- memcpy(&fflags, ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, 2);
290+ adp = ad_entry(ad, ADEID_FINDERI);
291+ AFP_ASSERT(adp != NULL);
292+ memcpy(&fflags, adp + FINDERINFO_FRFLAGOFF, 2);
293+
294 if (fflags & htons(FINDERINFO_INVISIBLE))
295 *attr |= htons(ATTRBIT_INVISIBLE);
296 else
297@@ -61,10 +70,15 @@ int ad_setattr(const struct adouble *ad,
298 attr &= ~(ATTRBIT_MULTIUSER | ATTRBIT_NOWRITE | ATTRBIT_NOCOPY);
299
300 if (ad_getentryoff(ad, ADEID_AFPFILEI) && ad_getentryoff(ad, ADEID_FINDERI)) {
301- memcpy(ad_entry(ad, ADEID_AFPFILEI) + AFPFILEIOFF_ATTR, &attr, sizeof(attr));
302+ char *adp = NULL;
303+
304+ adp = ad_entry(ad, ADEID_FINDERI);
305+ AFP_ASSERT(adp != NULL);
306+
307+ memcpy(adp + AFPFILEIOFF_ATTR, &attr, sizeof(attr));
308
309 /* Now set opaque flags in FinderInfo too */
310- memcpy(&fflags, ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, 2);
311+ memcpy(&fflags, adp + FINDERINFO_FRFLAGOFF, 2);
312 if (attr & htons(ATTRBIT_INVISIBLE))
313 fflags |= htons(FINDERINFO_INVISIBLE);
314 else
315@@ -77,7 +91,7 @@ int ad_setattr(const struct adouble *ad,
316 } else
317 fflags &= htons(~FINDERINFO_ISHARED);
318
319- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &fflags, 2);
320+ memcpy(adp + FINDERINFO_FRFLAGOFF, &fflags, 2);
321 }
322
323 return 0;
324@@ -86,54 +100,114 @@ int ad_setattr(const struct adouble *ad,
325 /* --------------
326 * save file/folder ID in AppleDoubleV2 netatalk private parameters
327 * return 1 if resource fork has been modified
328+ * return -1 on error.
329 */
330 int ad_setid (struct adouble *adp, const dev_t dev, const ino_t ino , const uint32_t id, const cnid_t did, const void *stamp)
331 {
332 uint32_t tmp;
333+ char *ade = NULL;
334
335 ad_setentrylen( adp, ADEID_PRIVID, sizeof(id));
336 tmp = id;
337 if (adp->ad_vers == AD_VERSION_EA)
338 tmp = htonl(tmp);
339- memcpy(ad_entry( adp, ADEID_PRIVID ), &tmp, sizeof(tmp));
340+
341+ ade = ad_entry(adp, ADEID_PRIVID);
342+ if (ade == NULL) {
343+ LOG(log_warning, logtype_ad, "ad_setid: failed to set ADEID_PRIVID\n");
344+ return -1;
345+ }
346+ memcpy(ade, &tmp, sizeof(tmp));
347
348 ad_setentrylen( adp, ADEID_PRIVDEV, sizeof(dev_t));
349+ ade = ad_entry(adp, ADEID_PRIVDEV);
350+ if (ade == NULL) {
351+ LOG(log_warning, logtype_ad, "ad_setid: failed to set ADEID_PRIVDEV\n");
352+ return -1;
353+ }
354+
355 if ((adp->ad_options & ADVOL_NODEV)) {
356- memset(ad_entry( adp, ADEID_PRIVDEV ), 0, sizeof(dev_t));
357+ memset(ade, 0, sizeof(dev_t));
358 } else {
359- memcpy(ad_entry( adp, ADEID_PRIVDEV ), &dev, sizeof(dev_t));
360+ memcpy(ade, &dev, sizeof(dev_t));
361 }
362
363 ad_setentrylen( adp, ADEID_PRIVINO, sizeof(ino_t));
364- memcpy(ad_entry( adp, ADEID_PRIVINO ), &ino, sizeof(ino_t));
365
366- ad_setentrylen( adp, ADEID_DID, sizeof(did));
367- memcpy(ad_entry( adp, ADEID_DID ), &did, sizeof(did));
368+ ade = ad_entry(adp, ADEID_PRIVINO);
369+ if (ade == NULL) {
370+ LOG(log_warning, logtype_ad, "ad_setid: failed to set ADEID_PRIVINO\n");
371+ return -1;
372+ }
373+ memcpy(ade, &ino, sizeof(ino_t));
374+
375+ if (adp->ad_vers != AD_VERSION_EA) {
376+ ad_setentrylen( adp, ADEID_DID, sizeof(did));
377+
378+ ade = ad_entry(adp, ADEID_DID);
379+ if (ade == NULL) {
380+ LOG(log_warning, logtype_ad, "ad_setid: failed to set ADEID_DID\n");
381+ return -1;
382+ }
383+ memcpy(ade, &did, sizeof(did));
384+ }
385
386 ad_setentrylen( adp, ADEID_PRIVSYN, ADEDLEN_PRIVSYN);
387- memcpy(ad_entry( adp, ADEID_PRIVSYN ), stamp, ADEDLEN_PRIVSYN);
388+ ade = ad_entry(adp, ADEID_PRIVSYN);
389+ if (ade == NULL) {
390+ LOG(log_warning, logtype_ad, "ad_setid: failed to set ADEID_PRIVSYN\n");
391+ return -1;
392+ }
393+ memcpy(ade, stamp, ADEDLEN_PRIVSYN);
394
395 return 1;
396 }
397
398-/* ----------------------------- */
399+/*
400+ * Retrieve stored file / folder. Callers should treat a return of CNID_INVALID (0) as an invalid value.
401+ */
402 uint32_t ad_getid (struct adouble *adp, const dev_t st_dev, const ino_t st_ino , const cnid_t did, const void *stamp _U_)
403 {
404 uint32_t aint = 0;
405 dev_t dev;
406 ino_t ino;
407- cnid_t a_did;
408+ cnid_t a_did = 0;
409
410 if (adp) {
411 if (sizeof(dev_t) == ad_getentrylen(adp, ADEID_PRIVDEV)) {
412- memcpy(&dev, ad_entry(adp, ADEID_PRIVDEV), sizeof(dev_t));
413- memcpy(&ino, ad_entry(adp, ADEID_PRIVINO), sizeof(ino_t));
414- memcpy(&a_did, ad_entry(adp, ADEID_DID), sizeof(cnid_t));
415+ char *ade = NULL;
416+ ade = ad_entry(adp, ADEID_PRIVDEV);
417+ if (ade == NULL) {
418+ LOG(log_warning, logtype_ad, "ad_getid: failed to retrieve ADEID_PRIVDEV\n");
419+ return CNID_INVALID;
420+ }
421+ memcpy(&dev, ade, sizeof(dev_t));
422+ ade = ad_entry(adp, ADEID_PRIVINO);
423+ if (ade == NULL) {
424+ LOG(log_warning, logtype_ad, "ad_getid: failed to retrieve ADEID_PRIVINO\n");
425+ return CNID_INVALID;
426+ }
427+ memcpy(&ino, ade, sizeof(ino_t));
428+
429+ if (adp->ad_vers != AD_VERSION_EA) {
430+ /* ADEID_DID is not stored for AD_VERSION_EA */
431+ ade = ad_entry(adp, ADEID_DID);
432+ if (ade == NULL) {
433+ LOG(log_warning, logtype_ad, "ad_getid: failed to retrieve ADEID_DID\n");
434+ return CNID_INVALID;
435+ }
436+ memcpy(&a_did, ade, sizeof(cnid_t));
437+ }
438
439 if (((adp->ad_options & ADVOL_NODEV) || (dev == st_dev))
440 && ino == st_ino
441- && (!did || a_did == did) ) {
442- memcpy(&aint, ad_entry(adp, ADEID_PRIVID), sizeof(aint));
443+ && (!did || a_did == 0 || a_did == did) ) {
444+ ade = ad_entry(adp, ADEID_PRIVID);
445+ if (ade == NULL) {
446+ LOG(log_warning, logtype_ad, "ad_getid: failed to retrieve ADEID_PRIVID\n");
447+ return CNID_INVALID;
448+ }
449+ memcpy(&aint, ade, sizeof(aint));
450 if (adp->ad_vers == AD_VERSION2)
451 return aint;
452 else
453@@ -141,7 +215,7 @@ uint32_t ad_getid (struct adouble *adp,
454 }
455 }
456 }
457- return 0;
458+ return CNID_INVALID;
459 }
460
461 /* ----------------------------- */
462@@ -150,13 +224,18 @@ uint32_t ad_forcegetid (struct adouble *
463 uint32_t aint = 0;
464
465 if (adp) {
466- memcpy(&aint, ad_entry(adp, ADEID_PRIVID), sizeof(aint));
467+ char *ade = NULL;
468+ ade = ad_entry(adp, ADEID_PRIVID);
469+ if (ade == NULL) {
470+ return CNID_INVALID;
471+ }
472+ memcpy(&aint, ade, sizeof(aint));
473 if (adp->ad_vers == AD_VERSION2)
474 return aint;
475 else
476 return ntohl(aint);
477 }
478- return 0;
479+ return CNID_INVALID;
480 }
481
482 /* -----------------
483@@ -168,8 +247,13 @@ int ad_setname(struct adouble *ad, const
484 if ((len = strlen(path)) > ADEDLEN_NAME)
485 len = ADEDLEN_NAME;
486 if (path && ad_getentryoff(ad, ADEID_NAME)) {
487+ char *ade = NULL;
488 ad_setentrylen( ad, ADEID_NAME, len);
489- memcpy(ad_entry( ad, ADEID_NAME ), path, len);
490+ ade = ad_entry(ad, ADEID_NAME);
491+ if (ade == NULL) {
492+ return -1;
493+ }
494+ memcpy(ade, path, len);
495 return 1;
496 }
497 return 0;
498--- a/libatalk/adouble/ad_conv.c
499+++ b/libatalk/adouble/ad_conv.c
500@@ -93,6 +93,7 @@ static int ad_conv_v22ea_hf(const char *
501 goto copy;
502 if (ad_getentryoff(&adv2, ADEID_FINDERI)
503 && (ad_getentrylen(&adv2, ADEID_FINDERI) == ADEDLEN_FINDERI)
504+ && (ad_entry(&adv2, ADEID_FINDERI) != NULL)
505 && (memcmp(ad_entry(&adv2, ADEID_FINDERI), emptyad, ADEDLEN_FINDERI) != 0))
506 goto copy;
507 if (ad_getentryoff(&adv2, ADEID_FILEDATESI)) {
508@@ -101,7 +102,7 @@ static int ad_conv_v22ea_hf(const char *
509 if ((ctime != mtime) || (mtime != sp->st_mtime))
510 goto copy;
511 }
512- if (ad_getentryoff(&adv2, ADEID_AFPFILEI)) {
513+ if (ad_getentryoff(&adv2, ADEID_AFPFILEI) && (ad_entry(&adv2, ADEID_AFPFILEI) != NULL)) {
514 if (memcmp(ad_entry(&adv2, ADEID_AFPFILEI), &afpinfo, ADEDLEN_AFPFILEI) != 0)
515 goto copy;
516 }
517@@ -115,6 +116,7 @@ copy:
518 EC_ZERO_LOGSTR( ad_open(&adea, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE),
519 "ad_conv_v22ea_hf(\"%s\"): error creating metadata EA: %s",
520 fullpathname(path), strerror(errno));
521+ AFP_ASSERT(ad_refresh(path, &adea) == 0);
522 EC_ZERO_LOG( ad_copy_header(&adea, &adv2) );
523 ad_flush(&adea);
524
525--- a/libatalk/adouble/ad_date.c
526+++ b/libatalk/adouble/ad_date.c
527@@ -10,6 +10,7 @@ int ad_setdate(struct adouble *ad,
528 unsigned int dateoff, uint32_t date)
529 {
530 int xlate = (dateoff & AD_DATE_UNIX);
531+ char *ade = NULL;
532
533 dateoff &= AD_DATE_MASK;
534 if (xlate)
535@@ -20,7 +21,12 @@ int ad_setdate(struct adouble *ad,
536
537 if (dateoff > AD_DATE_ACCESS)
538 return -1;
539- memcpy(ad_entry(ad, ADEID_FILEDATESI) + dateoff, &date, sizeof(date));
540+
541+ ade = ad_entry(ad, ADEID_FILEDATESI);
542+ if (ade == NULL) {
543+ return -1;
544+ }
545+ memcpy(ade + dateoff, &date, sizeof(date));
546
547 return 0;
548 }
549@@ -29,6 +35,7 @@ int ad_getdate(const struct adouble *ad,
550 unsigned int dateoff, uint32_t *date)
551 {
552 int xlate = (dateoff & AD_DATE_UNIX);
553+ char *ade = NULL;
554
555 dateoff &= AD_DATE_MASK;
556 if (!ad_getentryoff(ad, ADEID_FILEDATESI))
557@@ -36,7 +43,13 @@ int ad_getdate(const struct adouble *ad,
558
559 if (dateoff > AD_DATE_ACCESS)
560 return -1;
561- memcpy(date, ad_entry(ad, ADEID_FILEDATESI) + dateoff, sizeof(uint32_t));
562+
563+
564+ ade = ad_entry(ad, ADEID_FILEDATESI);
565+ if (ade == NULL) {
566+ return -1;
567+ }
568+ memcpy(date, ade + dateoff, sizeof(uint32_t));
569
570 if (xlate)
571 *date = AD_DATE_TO_UNIX(*date);
572--- a/libatalk/adouble/ad_flush.c
573+++ b/libatalk/adouble/ad_flush.c
574@@ -151,6 +151,7 @@ int ad_rebuild_adouble_header_osx(struct
575 uint32_t temp;
576 uint16_t nent;
577 char *buf;
578+ char *ade = NULL;
579
580 LOG(log_debug, logtype_ad, "ad_rebuild_adouble_header_osx");
581
582@@ -184,7 +185,10 @@ int ad_rebuild_adouble_header_osx(struct
583 memcpy(buf, &temp, sizeof( temp ));
584 buf += sizeof( temp );
585
586- memcpy(adbuf + ADEDOFF_FINDERI_OSX, ad_entry(ad, ADEID_FINDERI), ADEDLEN_FINDERI);
587+ ade = ad_entry(ad, ADEID_FINDERI);
588+ AFP_ASSERT(ade != NULL);
589+
590+ memcpy(adbuf + ADEDOFF_FINDERI_OSX, ade, ADEDLEN_FINDERI);
591
592 /* rfork */
593 temp = htonl( EID_DISK(ADEID_RFORK) );
594@@ -211,8 +215,12 @@ int ad_copy_header(struct adouble *add,
595 {
596 uint32_t eid;
597 uint32_t len;
598+ char *src = NULL;
599+ char *dst = NULL;
600
601 for ( eid = 0; eid < ADEID_MAX; eid++ ) {
602+ src = dst = NULL;
603+
604 if ( ads->ad_eid[ eid ].ade_off == 0 || add->ad_eid[ eid ].ade_off == 0 )
605 continue;
606
607@@ -226,17 +234,28 @@ int ad_copy_header(struct adouble *add,
608 continue;
609 default:
610 ad_setentrylen( add, eid, len );
611- memcpy( ad_entry( add, eid ), ad_entry( ads, eid ), len );
612+ dst = ad_entry(add, eid);
613+ AFP_ASSERT(dst != NULL);
614+
615+ src = ad_entry(ads, eid);
616+ AFP_ASSERT(src != NULL);
617+
618+ memcpy( dst, src, len );
619 }
620 }
621 add->ad_rlen = ads->ad_rlen;
622
623 if (((ads->ad_vers == AD_VERSION2) && (add->ad_vers == AD_VERSION_EA))
624 || ((ads->ad_vers == AD_VERSION_EA) && (add->ad_vers == AD_VERSION2))) {
625+ src = dst = NULL;
626 cnid_t id;
627- memcpy(&id, ad_entry(add, ADEID_PRIVID), sizeof(cnid_t));
628+
629+ dst = ad_entry(add, ADEID_PRIVID);
630+ AFP_ASSERT(dst != NULL);
631+
632+ memcpy(&id, dst, sizeof(cnid_t));
633 id = htonl(id);
634- memcpy(ad_entry(add, ADEID_PRIVID), &id, sizeof(cnid_t));
635+ memcpy(dst, &id, sizeof(cnid_t));
636 }
637 return 0;
638 }
639--- a/libatalk/adouble/ad_open.c
640+++ b/libatalk/adouble/ad_open.c
641@@ -140,17 +140,17 @@ static struct adouble_fops ad_adouble_ea
642
643 static const struct entry entry_order2[ADEID_NUM_V2 + 1] = {
644 {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
645- {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
646+ {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_COMMENT},
647 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
648 {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
649 {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
650 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
651 {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
652 {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
653- {ADEID_PRIVDEV, ADEDOFF_PRIVDEV, ADEDLEN_INIT},
654- {ADEID_PRIVINO, ADEDOFF_PRIVINO, ADEDLEN_INIT},
655- {ADEID_PRIVSYN, ADEDOFF_PRIVSYN, ADEDLEN_INIT},
656- {ADEID_PRIVID, ADEDOFF_PRIVID, ADEDLEN_INIT},
657+ {ADEID_PRIVDEV, ADEDOFF_PRIVDEV, ADEDLEN_PRIVDEV},
658+ {ADEID_PRIVINO, ADEDOFF_PRIVINO, ADEDLEN_PRIVINO},
659+ {ADEID_PRIVSYN, ADEDOFF_PRIVSYN, ADEDLEN_PRIVSYN},
660+ {ADEID_PRIVID, ADEDOFF_PRIVID, ADEDLEN_PRIVID},
661 {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
662 {0, 0, 0}
663 };
664@@ -158,13 +158,13 @@ static const struct entry entry_order2[A
665 /* Using Extended Attributes */
666 static const struct entry entry_order_ea[ADEID_NUM_EA + 1] = {
667 {ADEID_FINDERI, ADEDOFF_FINDERI_EA, ADEDLEN_FINDERI},
668- {ADEID_COMMENT, ADEDOFF_COMMENT_EA, ADEDLEN_INIT},
669+ {ADEID_COMMENT, ADEDOFF_COMMENT_EA, ADEDLEN_COMMENT},
670 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_EA, ADEDLEN_FILEDATESI},
671 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_EA, ADEDLEN_AFPFILEI},
672- {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_EA, ADEDLEN_INIT},
673- {ADEID_PRIVINO, ADEDOFF_PRIVINO_EA, ADEDLEN_INIT},
674- {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_EA, ADEDLEN_INIT},
675- {ADEID_PRIVID, ADEDOFF_PRIVID_EA, ADEDLEN_INIT},
676+ {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_EA, ADEDLEN_PRIVDEV},
677+ {ADEID_PRIVINO, ADEDOFF_PRIVINO_EA, ADEDLEN_PRIVINO},
678+ {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_EA, ADEDLEN_PRIVSYN},
679+ {ADEID_PRIVID, ADEDOFF_PRIVID_EA, ADEDLEN_PRIVID},
680 {0, 0, 0}
681 };
682
683@@ -360,15 +360,22 @@ static int new_ad_header(struct adouble
684 const struct entry *eid;
685 uint16_t ashort;
686 struct stat st;
687+ char *adp = NULL;
688
689 LOG(log_debug, logtype_ad, "new_ad_header(\"%s\")", path);
690
691 if (ad_init_offsets(ad) != 0)
692 return -1;
693
694+ if (ad->valid_data_len == 0) {
695+ ad->valid_data_len = ad->ad_vers == AD_VERSION_EA ? AD_DATASZ_EA : AD_DATASZ2;
696+ }
697+ adp = ad_entry(ad, ADEID_FINDERI);
698+ AFP_ASSERT(adp != NULL);
699+
700 /* set default creator/type fields */
701- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
702- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
703+ memcpy(adp + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
704+ memcpy(adp + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
705
706 /* make things invisible */
707 if ((ad->ad_options & ADVOL_INVDOTS)
708@@ -378,14 +385,16 @@ static int new_ad_header(struct adouble
709 ashort = htons(ATTRBIT_INVISIBLE);
710 ad_setattr(ad, ashort);
711 ashort = htons(FINDERINFO_INVISIBLE);
712- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
713+ memcpy(adp + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
714 }
715
716 /* put something sane in the date fields */
717 if (stp == NULL) {
718 stp = &st;
719- if (lstat(path, &st) != 0)
720+ if (lstat(path, &st) != 0) {
721+ ad->valid_data_len = 0;
722 return -1;
723+ }
724 }
725 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, stp->st_mtime);
726 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, stp->st_mtime);
727@@ -417,7 +426,7 @@ static int parse_entries(struct adouble
728
729 if (!eid
730 || eid > ADEID_MAX
731- || off >= valid_data_len
732+ || ((eid != ADEID_RFORK) && (off >= valid_data_len))
733 || ((eid != ADEID_RFORK) && (off + len > valid_data_len)))
734 {
735 LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %u, off: %u, len: %u",
736@@ -782,16 +791,41 @@ static int ad_header_read_ea(const char
737 EC_FAIL;
738 }
739
740+ /*
741+ * It is possible for AFP metadata to contain a zero-length
742+ * comment. This will cause ad_entry(ad, ADEID_COMMENT) to return NULL
743+ * but should not be treated as an error condition.
744+ * Since recent CVE fixes have introduced new behavior regarding
745+ * ad_entry() output. For now, we will AFP_ASSERT() in EC_CLEANUP to prevent
746+ * altering on-disk info. This does introduce an avenue to DOS
747+ * the netatalk server by locally writing garbage to the EA. At this
748+ * point, the outcome is an acceptable risk to prevent unintended
749+ * changes to metadata.
750+ */
751 if (nentries != ADEID_NUM_EA
752 || !ad_entry(ad, ADEID_FINDERI)
753+#if 0
754 || !ad_entry(ad, ADEID_COMMENT)
755+#endif
756 || !ad_entry(ad, ADEID_FILEDATESI)
757 || !ad_entry(ad, ADEID_AFPFILEI)
758 || !ad_entry(ad, ADEID_PRIVDEV)
759 || !ad_entry(ad, ADEID_PRIVINO)
760 || !ad_entry(ad, ADEID_PRIVSYN)
761 || !ad_entry(ad, ADEID_PRIVID)) {
762- LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): invalid metadata EA", fullpathname(path));
763+ LOG(log_error, logtype_ad,
764+ "ad_header_read_ea(\"%s\"): invalid metadata EA "
765+ "this is now being treated as a fatal error. "
766+ "if you see this log entry, please file a bug ticket "
767+ "with your upstream vendor and attach the generated "
768+ "core file.", path ? fullpathname(path) : "UNKNOWN");
769+
770+ errno = EINVAL;
771+ EC_FAIL;
772+ }
773+
774+ if (!ad_entry(ad, ADEID_COMMENT) &&
775+ (ad->ad_eid[ADEID_COMMENT].ade_len != 0)) {
776 errno = EINVAL;
777 EC_FAIL;
778 }
779@@ -805,6 +839,8 @@ static int ad_header_read_ea(const char
780 #endif
781
782 EC_CLEANUP:
783+ AFP_ASSERT(!(ret != 0 && errno == EINVAL));
784+#if 0
785 if (ret != 0 && errno == EINVAL) {
786 become_root();
787 (void)sys_removexattr(path, AD_EA_META);
788@@ -812,6 +848,7 @@ EC_CLEANUP:
789 LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): deleted invalid metadata EA", fullpathname(path), nentries);
790 errno = ENOENT;
791 }
792+#endif
793 EC_EXIT;
794 }
795