windows-handle-invalid-utf16-names-20081019
authorAsanka Herath <asanka@secure-endpoints.com>
Mon, 20 Oct 2008 00:17:41 +0000 (00:17 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Mon, 20 Oct 2008 00:17:41 +0000 (00:17 +0000)
LICENSE MIT
FIXES 116641

Windows will deliver to OpenAFS UTF16 strings that are not valid Unicode
and cannot be converted to UTF8.  Return bad file name errors in those
cases.

Make sure all file server strings once converted to UTF16 can be converted
back.  If not, escape the invalid portions of the string so that the
file can be accessed.

src/WINNT/afsd/cm_btree.c
src/WINNT/afsd/cm_dir.c
src/WINNT/afsd/cm_ioctl.c
src/WINNT/afsd/cm_nls.c
src/WINNT/afsd/cm_nls.h
src/WINNT/afsd/cm_vnodeops.c
src/WINNT/afsd/cm_volstat.c
src/WINNT/afsd/smb.c
src/WINNT/afsd/smb3.c

index c7bc2b7..ea0ec3a 100644 (file)
@@ -1588,6 +1588,10 @@ cm_BPlusDirLookupOriginalName(cm_dirOp_t * op, clientchar_t *centry,
     }
 
     entry = cm_ClientStringToNormStringAlloc(centry, -1, NULL);
+    if (!entry) {
+        rc = EINVAL;
+        goto done;
+    }
     key.name = entry;
 
     lock_AssertAny(&op->scp->dirlock);
@@ -1680,6 +1684,10 @@ cm_BPlusDirLookup(cm_dirOp_t * op, clientchar_t * centry, cm_fid_t * cfid)
     }
 
     entry = cm_ClientStringToNormStringAlloc(centry, -1, NULL);
+    if (!entry) {
+        rc = EINVAL;
+        goto done;
+    }
     key.name = entry;
 
     lock_AssertAny(&op->scp->dirlock);
@@ -1767,6 +1775,10 @@ long cm_BPlusDirCreateEntry(cm_dirOp_t * op, clientchar_t * entry, cm_fid_t * cf
     }
 
     normalizedName = cm_ClientStringToNormStringAlloc(entry, -1, NULL);
+    if (!normalizedName) {
+        rc = EINVAL;
+        goto done;
+    }
     key.name = normalizedName;
 
     lock_AssertWrite(&op->scp->dirlock);
@@ -1833,6 +1845,10 @@ int  cm_BPlusDirDeleteEntry(cm_dirOp_t * op, clientchar_t *centry)
     }
 
     normalizedEntry = cm_ClientStringToNormStringAlloc(centry, -1, NULL);
+    if (!normalizedEntry) {
+        rc = EINVAL;
+        goto done;
+    }
     key.name = normalizedEntry;
 
     lock_AssertWrite(&op->scp->dirlock);
@@ -2000,6 +2016,12 @@ int cm_BPlusDirFoo(struct cm_scache *scp, struct cm_dirEntry *dep,
     }
 
     data.cname = cm_FsStringToClientStringAlloc(dep->name, -1, NULL);
+    if (data.cname == NULL) {
+#ifdef DEBUG
+        DebugBreak();
+#endif
+        return 0;
+    }
     data.fsname = cm_FsStrDup(dep->name);
     data.shortform = FALSE;
 
@@ -2017,10 +2039,12 @@ int cm_BPlusDirFoo(struct cm_scache *scp, struct cm_dirEntry *dep,
 
         key.name = wshortName;
         data.cname = cm_FsStringToClientStringAlloc(dep->name, -1, NULL);
-        data.fsname = cm_FsStrDup(dep->name);
-        data.shortform = TRUE;
+        if (data.cname) {
+            data.fsname = cm_FsStrDup(dep->name);
+            data.shortform = TRUE;
 
-        insert(scp->dirBplus, key, data);
+            insert(scp->dirBplus, key, data);
+        }
     }
 
     if (normalized_name)
index edc4fc5..5f2439c 100644 (file)
@@ -1293,7 +1293,6 @@ cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
                               CM_SCACHESYNC_NEEDCALLBACK |
                               (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
                               CM_SCACHESYNC_BUFLOCKED);
-
             code = CM_ERROR_NOTINCACHE;
         }
 
index 1d78f0c..c24ce06 100644 (file)
@@ -457,9 +457,13 @@ cm_IoctlGetFileCellName(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
             clientchar_t * cellname;
 
             cellname = cm_FsStringToClientStringAlloc(cellp->name, -1, NULL); 
+            if (cellname == NULL) {
+                code = CM_ERROR_NOSUCHCELL;
+            } else {
             cm_UnparseIoctlString(ioctlp, NULL, cellname, -1);
             free(cellname);
             code = 0;
+            }
         } else
             code = CM_ERROR_NOSUCHCELL;
     }
@@ -1354,8 +1358,12 @@ cm_IoctlGetCell(struct cm_ioctl *ioctlp, struct cm_user *userp)
         ioctlp->outDatap = basep + max * sizeof(afs_int32);
 
         cellnamep = cm_FsStringToClientStringAlloc(tcellp->name, -1, NULL);
+        if (cellnamep) {
         cm_UnparseIoctlString(ioctlp, NULL, cellnamep, -1);
         free(cellnamep);
+        } else {
+            tcellp = NULL;
+        }
     }
 
     if (tcellp) 
@@ -1452,9 +1460,13 @@ cm_IoctlGetWsCell(cm_ioctl_t *ioctlp, cm_user_t *userp)
     } else if (cm_data.rootCellp) {
         clientchar_t * cellnamep = cm_FsStringToClientStringAlloc(cm_data.rootCellp->name, -1, NULL);
         /* return the default cellname to the caller */
+        if (cellnamep) {
         cm_UnparseIoctlString(ioctlp, NULL, cellnamep, -1);
         free(cellnamep);
     } else {
+            code = CM_ERROR_NOSUCHCELL;
+        }
+    } else {
         /* if we don't know our default cell, return failure */
         code = CM_ERROR_NOSUCHCELL;
     }   
index 8d3eb21..63e56e4 100644 (file)
@@ -550,6 +550,76 @@ static int sanitize_bytestring(const char * src, int cch_src,
     return (int)(dest - odest);
 }
 
+static int sanitize_utf16char(wchar_t c, wchar_t ** pdest, size_t * pcch)
+{
+    if (*pcch >= 6) {
+        StringCchPrintfExW(*pdest, *pcch, pdest, pcch, 0, L"%%%04x", (int) c);
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static int sanitize_utf16string(const wchar_t * src, size_t cch_src,
+                                wchar_t * dest, size_t cch_dest)
+{
+    int cch_dest_o = cch_dest;
+
+    if (dest == NULL) {
+        /* only estimating */
+        for (cch_dest = 0; cch_src > 0;) {
+            if (*src >= 0xd800 && *src < 0xdc00) {
+                if (cch_src <= 1 || src[1] < 0xdc00 || src[1] > 0xdfff) {
+                    /* dangling surrogate */
+                    src++;
+                    cch_src --;
+                    cch_dest += 5;
+                } else {
+                    /* surrogate pair */
+                    src += 2;
+                    cch_src -= 2;
+                    cch_dest += 2;
+                }
+            } else if (*src >= 0xdc00 && *src <= 0xdfff) {
+                /* dangling surrogate */
+                src++;
+                cch_src --;
+                cch_dest += 5;
+            } else {
+                /* normal char */
+                src++; cch_src --;
+                cch_dest++;
+            }
+        }
+
+        return cch_dest;
+    }
+
+    while (cch_src > 0 && cch_dest > 0) {
+        if (*src >= 0xd800 && *src < 0xdc00) {
+            if (cch_src <= 1 || src[1] < 0xdc00 || src[1] > 0xdfff) {
+                if (!sanitize_utf16char(*src++, &dest, &cch_dest))
+                    return 0;
+                cch_src--;
+            } else {
+                /* found a surrogate pair */
+                *dest++ = *src++;
+                *dest++ = *src++;
+                cch_dest -= 2; cch_src -= 2;
+            }
+        } else if (*src >= 0xdc00 && *src <= 0xdfff) {
+            if (!sanitize_utf16char(*src++, &dest, &cch_dest))
+                return 0;
+            cch_src--;
+        } else {
+            *dest++ = *src++;
+            cch_dest--; cch_src--;
+        }
+    }
+
+    return (cch_src == 0) ? cch_dest_o - cch_dest : 0;
+}
+
 #undef Esc
 #undef IS_ESCAPED
 #undef ESCVAL
@@ -575,6 +645,10 @@ long cm_NormalizeUtf8StringToUtf16(const char * src, int cch_src,
         return 1;
     }
 
+    if (dest && cch_dest > 0) {
+        dest[0] = L'\0';
+    }
+
     if (cch_src == -1) {
         cch_src = strlen(src) + 1;
     }
@@ -582,6 +656,18 @@ long cm_NormalizeUtf8StringToUtf16(const char * src, int cch_src,
     cch = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src,
                               cch_src * sizeof(char), wsrcbuf, NLSMAXCCH);
 
+    if (cch != 0 && !cm_is_valid_utf16(wsrcbuf, cch)) {
+        wchar_t wsanitized[NLSMAXCCH];
+
+        /* We successfully converted, but the resulting UTF-16 string
+           has dangling surrogates.  We should try and escape those
+           next.  */
+        cch = sanitize_utf16string(wsrcbuf, cch, wsanitized, NLSMAXCCH);
+        if (cch != 0) {
+            memcpy(wsrcbuf, wsanitized, cch * sizeof(wchar_t));
+        }
+    }
+
     if (cch == 0) {
         if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
             char sanitized[NLSMAXCCH];
@@ -665,6 +751,18 @@ cm_normchar_t *cm_NormalizeUtf8StringToUtf16Alloc(const cm_utf8char_t * src, int
     cch = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src,
                               cch_src * sizeof(char), wsrcbuf, NLSMAXCCH);
 
+    if (cch != 0 && !cm_is_valid_utf16(wsrcbuf, cch)) {
+        wchar_t wsanitized[NLSMAXCCH];
+
+        /* We successfully converted, but the resulting UTF-16 string
+           has dangling surrogates.  We should try and escape those
+           next.  */
+        cch = sanitize_utf16string(wsrcbuf, cch, wsanitized, NLSMAXCCH);
+        if (cch != 0) {
+            memcpy(wsrcbuf, wsanitized, cch * sizeof(wchar_t));
+        }
+    }
+
     if (cch == 0) {
         if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
             char sanitized[NLSMAXCCH];
@@ -720,6 +818,10 @@ int cm_Utf8ToUtf16(const cm_utf8char_t * src, int cch_src,
 {
     int cch;
 
+    if (cch_dest >= 1 && dest != NULL) {
+        dest[0] = L'\0';
+    }
+
     if (!nls_init)
         cm_InitNormalization();
 
@@ -730,6 +832,15 @@ int cm_Utf8ToUtf16(const cm_utf8char_t * src, int cch_src,
     cch = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src,
                               cch_src * sizeof(char), dest, cch_dest);
 
+    if (cch != 0 && !cm_is_valid_utf16(dest, cch)) {
+        wchar_t wsanitized[NLSMAXCCH];
+
+        cch = sanitize_utf16string(dest, cch, wsanitized, NLSMAXCCH);
+        if (cch != 0) {
+            memcpy(dest, wsanitized, cch * sizeof(wchar_t));
+        }
+    }
+
     if (cch == 0) {
         if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
             char sanitized[NLSMAXCCH];
@@ -838,6 +949,28 @@ cm_unichar_t  * cm_Utf8ToUtf16Alloc(const cm_utf8char_t * src, int cch_src, int
         cch = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src,
                                   cch_src * sizeof(char), ustr, cch);
         ustr[cch] = 0;
+
+        if (!cm_is_valid_utf16(ustr, cch)) {
+            cm_unichar_t * us = NULL;
+            int cch_s;
+
+            cch_s = sanitize_utf16string(ustr, cch, NULL, 0);
+            if (cch_s != 0) {
+                us = malloc(cch_s * sizeof(wchar_t));
+                cch_s = sanitize_utf16string(ustr, cch, us, cch_s);
+            }
+
+            if (cch_s != 0) {
+                free(ustr);
+                ustr = us;
+                us = NULL;
+            } else {
+                if (us)
+                    free(us);
+                free(ustr);
+                ustr = NULL;
+            }
+        }
     }
 
     if (pcch_dest)
@@ -897,6 +1030,15 @@ long cm_NormalizeUtf8String(const char * src, int cch_src,
     cch = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src,
                               cch_src * sizeof(char), wsrcbuf, NLSMAXCCH);
 
+    if (cch != 0 && !cm_is_valid_utf16(wsrcbuf, cch)) {
+        wchar_t wsanitized[NLSMAXCCH];
+
+        cch = sanitize_utf16string(wsrcbuf, cch, wsanitized, NLSMAXCCH);
+        if (cch != 0) {
+            memcpy(wsrcbuf, wsanitized, cch * sizeof(wchar_t));
+        }
+    }
+
     if (cch == 0) {
         if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
             char sanitized[NLSMAXCCH];
@@ -1265,3 +1407,49 @@ wchar_t * char_this_utf16(const wchar_t * c)
     return (wchar_t *) c;
 }
 
+int cm_is_valid_utf16(const wchar_t * c, int cch)
+{
+    if (cch < 0)
+        cch = wcslen(c) + 1;
+
+    for (; cch > 0; c++, cch--) {
+        if (*c >= 0xd800 && *c < 0xdc00) {
+            c++; cch--;
+            if (cch == 0 || *c < 0xdc00 || *c > 0xdfff)
+                return 0;
+        } else if (*c >= 0xdc00 && *c <= 0xdfff) {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+#ifdef DEBUG
+wchar_t * cm_GetRawCharsAlloc(const wchar_t * c, int len)
+{
+    wchar_t * ret;
+    wchar_t * current;
+    size_t cb;
+
+    if (len == -1)
+        len = wcslen(c);
+
+    if (len == 0)
+        return wcsdup(L"(empty)");
+
+    cb = len * 5 * sizeof(wchar_t);
+    current = ret = malloc(cb);
+    if (ret == NULL)
+        return NULL;
+
+    for (; len > 0; ++c, --len) {
+        StringCbPrintfExW(current, cb, &current, &cb, 0,
+                         L"%04x", (int) *c);
+        if (len > 1)
+            StringCbCatExW(current, cb, L",", &current, &cb, 0);
+    }
+
+    return ret;
+}
+#endif
index 017d271..3d7517e 100644 (file)
@@ -132,12 +132,25 @@ typedef cm_normchar_t normchar_t;
 #define cm_NormStrCmp wcscmp
 #define cm_NormCharUpr towupper
 
+#define cm_IsValidClientString(s) cm_is_valid_utf16((s), -1)
+#define cm_IsValidNormString(s) cm_is_valid_utf16((s), -1)
+
 #define cm_Utf16ToClientString cm_Utf16ToUtf16
 
 extern long cm_InitNormalization(void);
 
 /* Functions annotated in accordance with sal.h */
 
+#ifndef __in_z
+
+#define __out_ecount_full_z(x)
+#define __out_ecount_full_z_opt(x)
+#define __in_z
+#define __out_z
+#define __inout_z
+
+#endif
+
 extern __out_ecount_full_z(*pcch_dest) __checkReturn __success(return != NULL) cm_normchar_t *
     cm_NormalizeStringAlloc
     (__in_ecount(cch_src) const cm_unichar_t * s,
@@ -254,6 +267,13 @@ cm_strlwr_utf16(__inout_z cm_unichar_t * str);
 extern __out_z cm_unichar_t *
 cm_strupr_utf16(__inout_z cm_unichar_t * str);
 
+extern int
+cm_is_valid_utf16(__in_z const wchar_t * c, int cch);
+
+#ifdef DEBUG
+wchar_t * cm_GetRawCharsAlloc(const wchar_t * c, int len);
+#endif
+
 #if 0
 
 extern long cm_NormalizeUtf16StringToUtf8(const wchar_t * src, int cch_src,
index 82b57a8..e9d8d54 100644 (file)
@@ -727,7 +727,11 @@ long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
 
     sp = (cm_lookupSearch_t *) rockp;
 
-    cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName));
+    if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
+        /* Can't normalize FS string. */
+        return 0;
+    }
+
     if (sp->caseFold)
         match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
     else
@@ -1040,7 +1044,15 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
     }
 
     nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
+    if (!nnamep) {
+        code = CM_ERROR_NOSUCHFILE;
+        goto done;
+    }
     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
+    if (!fnamep) {
+        code = CM_ERROR_NOSUCHFILE;
+        goto done;
+    }
 
     if (flags & CM_FLAG_NOMOUNTCHASE) {
         /* In this case, we should go and call cm_Dir* functions
@@ -1235,6 +1247,7 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
             if (nnamep) 
                 free(nnamep);
             nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
+            if (nnamep)
             cm_dnlcEnter(dscp, nnamep, tscp);
         }
         lock_ReleaseRead(&dscp->rw);
@@ -1755,12 +1768,19 @@ long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
         StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
         StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
     }
+
     if (code == 0) {
         clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
+        if (cpath != NULL) {
         cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
         free(cpath);
         *newSpaceBufferp = tsp;
     } else {
+            code = CM_ERROR_NOSUCHPATH;
+        }
+    } 
+
+    if (code != 0) {
         cm_FreeSpace(tsp);
 
         if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
index 3f1a2f6..3336aae 100644 (file)
@@ -325,6 +325,13 @@ cm_VolStatus_Path_To_ID(const char * share, const char * path, afs_uint32 * cell
     cpath = cm_FsStringToClientStringAlloc(path, -1, NULL);
     cshare = cm_FsStringToClientStringAlloc(share, -1, NULL);
 
+    if (cpath == NULL || cshare == NULL) {
+        osi_Log1(afsd_logp, "Can't convert %s string. Aborting",
+                 (cpath == NULL)? "path" : "share");
+        code = CM_ERROR_NOSUCHPATH;
+        goto done;
+    }
+
     code = cm_NameI(cm_data.rootSCachep, cpath,
                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                     cm_rootUserp, cshare, &req, &scp);
@@ -385,6 +392,13 @@ cm_VolStatus_Path_To_DFSlink(const char * share, const char * path, afs_uint32 *
     cpath = cm_FsStringToClientStringAlloc(path, -1, NULL);
     cshare = cm_FsStringToClientStringAlloc(share, -1, NULL);
 
+    if (cpath == NULL || cshare == NULL) {
+        osi_Log1(afsd_logp, "Can't convert %s string. Aborting",
+                 (cpath == NULL)? "path" : "share");
+        code = CM_ERROR_NOSUCHPATH;
+        goto done;
+    }
+
     code = cm_NameI(cm_data.rootSCachep, cpath, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW, 
                     cm_rootUserp, cshare, &req, &scp);
     if (code)
index f66cc05..d1d4ac9 100644 (file)
@@ -1763,7 +1763,11 @@ long smb_FindShareProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
     smb_findShare_rock_t * vrock = (smb_findShare_rock_t *) rockp;
     normchar_t normName[MAX_PATH];
 
-    cm_FsStringToNormString(dep->name, -1, normName, sizeof(normName)/sizeof(normName[0]));
+    if (cm_FsStringToNormString(dep->name, -1, normName, sizeof(normName)/sizeof(normName[0])) == 0) {
+        osi_Log1(smb_logp, "Skipping entry [%s]. Can't normalize FS string",
+                 osi_LogSaveString(smb_logp, dep->name));
+        return 0;
+    }
 
     if (!cm_ClientStrCmpNI(normName, vrock->shareName, 12)) {
         if(!cm_ClientStrCmpI(normName, vrock->shareName))
@@ -1956,6 +1960,8 @@ int smb_FindShare(smb_vc_t *vcp, smb_user_t *uidp,
         thyper.LowPart = 0;
 
         vrock.shareName = cm_ClientStringToNormStringAlloc(shareName, -1, NULL);
+        if (vrock.shareName == NULL) 
+            return 0;
         vrock.match = NULL;
         vrock.matchType = 0;
 
@@ -1996,13 +2002,14 @@ int smb_FindShare(smb_vc_t *vcp, smb_user_t *uidp,
         if (code == 0) {
             clientchar_t temp[1024];
 
-            cm_FsStringToClientString(ftemp, -1, temp, 1024);
+            if (cm_FsStringToClientString(ftemp, -1, temp, 1024) != 0) {
             cm_ClientStrPrintfN(pathName, (int)lengthof(pathName),
                                 rw ? _C("/.%S/") : _C("/%S/"), temp);
             *pathNamep = cm_ClientStrDup(cm_ClientStrLwr(pathName));
             return 1;
         }
     }
+    }
     /* failure */
     *pathNamep = NULL;
     return 0;
@@ -4686,7 +4693,8 @@ long smb_ReceiveCoreSearchDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
     code = 0;
     returnedNames = 0;
     while (1) {
-        clientchar_t *actualName;
+        clientchar_t *actualName = NULL;
+        int           free_actualName = 0;
         clientchar_t shortName[13];
         clientchar_t *shortNameEnd;
 
@@ -4834,6 +4842,16 @@ long smb_ReceiveCoreSearchDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
             free(actualName);
             cm_Gen8Dot3NameInt(dep->name, &dep->fid, shortName, &shortNameEnd);
             actualName = shortName;
+            free_actualName = 0;
+        } else {
+            free_actualName = 1;
+        }
+
+        if (actualName == NULL) {
+            /* Couldn't convert the name for some reason */
+            osi_Log1(smb_logp, "SMB search dir skipping entry :[%s]",
+                     osi_LogSaveString(smb_logp, dep->name));
+            goto nextEntry;
         }
 
         osi_Log3(smb_logp, "SMB search dir vn %d name %s (%S)",
@@ -4934,6 +4952,11 @@ long smb_ReceiveCoreSearchDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
         }      /* if we're including this name */
 
       nextEntry:
+        if (free_actualName && actualName) {
+            free(actualName);
+            actualName = NULL;
+        }
+
         /* and adjust curOffset to be where the new cookie is */
         thyper.HighPart = 0;
         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
@@ -5397,6 +5420,21 @@ long smb_ReceiveCoreOpen(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     }
 #endif
 
+    if (!cm_IsValidClientString(pathp)) {
+#ifdef DEBUG
+        clientchar_t * hexp;
+
+        hexp = cm_GetRawCharsAlloc(pathp, -1);
+        osi_Log1(smb_logp, "CoreOpen rejecting invalid name. [%S]",
+                 osi_LogSaveClientString(smb_logp, hexp));
+        if (hexp)
+            free(hexp);
+#else
+        osi_Log0(smb_logp, "CoreOpen rejecting invalid name");
+#endif
+        return CM_ERROR_BADNTFILENAME;
+    }
+
     share = smb_GetSMBParm(inp, 0);
     attribute = smb_GetSMBParm(inp, 1);
 
@@ -5538,7 +5576,13 @@ int smb_UnlinkProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hype
     if (!(rockp->vcp->flags & SMB_VCFLAG_USEV3))
         caseFold |= CM_FLAG_8DOT3;
 
-    cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName));
+    if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
+        /* Can't convert name */
+        osi_Log1(smb_logp, "Skipping entry [%s]. Can't normalize FS string.",
+                 osi_LogSaveString(smb_logp, dep->name));
+        return 0;
+    }
+
     match = cm_MatchMask(matchName, rockp->maskp, caseFold);
     if (!match &&
         (rockp->flags & SMB_MASKFLAG_TILDE) &&
@@ -5584,6 +5628,7 @@ long smb_ReceiveCoreUnlink(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     cm_req_t req;
 
     smb_InitReq(&req);
+    memset(&rock, 0, sizeof(rock));
 
     attribute = smb_GetSMBParm(inp, 0);
         
@@ -5632,6 +5677,10 @@ long smb_ReceiveCoreUnlink(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     rock.any = 0;
     rock.maskp = cm_ClientStringToNormStringAlloc(smb_FindMask(pathp), -1, NULL);
+    if (!rock.maskp) {
+        code = CM_ERROR_NOSUCHFILE;
+        goto done;
+    }
     rock.flags = ((cm_ClientStrChr(rock.maskp, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
 
     thyper.LowPart = 0;
@@ -5672,6 +5721,8 @@ long smb_ReceiveCoreUnlink(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
             osi_Log1(smb_logp, "Unlinking %s",
                      osi_LogSaveString(smb_logp, entry->name));
 
+            /* We assume this works because entry->name was
+               successfully converted in smb_UnlinkProc() once. */
             cm_FsStringToNormString(entry->name, -1,
                                     normalizedName, lengthof(normalizedName));
 
@@ -5686,10 +5737,14 @@ long smb_ReceiveCoreUnlink(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     cm_DirEntryListFree(&rock.matches);
 
+  done:
+    if (userp)
     cm_ReleaseUser(userp);
         
+    if (dscp)
     cm_ReleaseSCache(dscp);
 
+    if (rock.maskp)
     free(rock.maskp);
 
     if (code == 0 && !rock.any)
@@ -5721,7 +5776,13 @@ int smb_RenameProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hype
 
     rockp = (smb_renameRock_t *) vrockp;
 
-    cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName));
+    if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
+        /* Can't convert string */
+        osi_Log1(smb_logp, "Skpping entry [%s]. Can't normalize FS string",
+                 osi_LogSaveString(smb_logp, dep->name));
+        return 0;
+    }
+
     caseFold = ((rockp->flags & SMB_MASKFLAG_CASEFOLD)? CM_FLAG_CASEFOLD : 0);
     if (!(rockp->vcp->flags & SMB_VCFLAG_USEV3))
         caseFold |= CM_FLAG_8DOT3;
@@ -5775,6 +5836,8 @@ smb_Rename(smb_vc_t *vcp, smb_packet_t *inp, clientchar_t * oldPathp, clientchar
     }
 
     smb_InitReq(&req);
+    memset(&rock, 0, sizeof(rock));
+
     spacep = inp->spacep;
     smb_StripLastComponent(spacep->wdata, &oldLastNamep, oldPathp);
 
@@ -5840,19 +5903,6 @@ smb_Rename(smb_vc_t *vcp, smb_packet_t *inp, clientchar_t * oldPathp, clientchar
 
     /* TODO: The old name could be a wildcard.  The new name must not be */
 
-    /* do the vnode call */
-    rock.odscp = oldDscp;
-    rock.ndscp = newDscp;
-    rock.userp = userp;
-    rock.reqp = &req;
-    rock.vcp = vcp;
-    rock.maskp = cm_ClientStringToNormStringAlloc(oldLastNamep, -1, NULL);
-    rock.flags = ((cm_ClientStrChr(oldLastNamep, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
-    rock.newNamep = newLastNamep;
-    rock.fsOldName[0] = '\0';
-    rock.clOldName[0] = '\0';
-    rock.any = 0;
-
     /* Check if the file already exists; if so return error */
     code = cm_Lookup(newDscp,newLastNamep,CM_FLAG_CHECKPATH,userp,&req,&tmpscp);
     if ((code != CM_ERROR_NOSUCHFILE) && (code != CM_ERROR_BPLUS_NOMATCH) &&
@@ -5886,15 +5936,27 @@ smb_Rename(smb_vc_t *vcp, smb_packet_t *inp, clientchar_t * oldPathp, clientchar
 
         if (tmpscp != NULL)
             cm_ReleaseSCache(tmpscp);
-        cm_ReleaseSCache(newDscp);
-        cm_ReleaseSCache(oldDscp);
-        cm_ReleaseUser(userp);
 
-        free(rock.maskp);
-        rock.maskp = NULL;
-        return code; 
+        goto done;
     }
 
+    /* do the vnode call */
+    rock.odscp = oldDscp;
+    rock.ndscp = newDscp;
+    rock.userp = userp;
+    rock.reqp = &req;
+    rock.vcp = vcp;
+    rock.maskp = cm_ClientStringToNormStringAlloc(oldLastNamep, -1, NULL);
+    if (!rock.maskp) {
+        code = CM_ERROR_NOSUCHFILE;
+        goto done;
+    }
+    rock.flags = ((cm_ClientStrChr(oldLastNamep, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
+    rock.newNamep = newLastNamep;
+    rock.fsOldName[0] = '\0';
+    rock.clOldName[0] = '\0';
+    rock.any = 0;
+
     /* Now search the directory for the pattern, and do the appropriate rename when found */
     thyper.LowPart = 0;                /* search dir from here */
     thyper.HighPart = 0;
@@ -5946,14 +6008,17 @@ smb_Rename(smb_vc_t *vcp, smb_packet_t *inp, clientchar_t * oldPathp, clientchar
         }
     }
 
+  done:
     if (tmpscp != NULL) 
         cm_ReleaseSCache(tmpscp);
-    cm_ReleaseUser(userp);
-    cm_ReleaseSCache(oldDscp);
-    cm_ReleaseSCache(newDscp);
-
-    free(rock.maskp);
-    rock.maskp = NULL;
+    if (userp)
+        cm_ReleaseUser(userp);
+    if (oldDscp)
+        cm_ReleaseSCache(oldDscp);
+    if (newDscp)
+        cm_ReleaseSCache(newDscp);
+    if (rock.maskp)
+        free(rock.maskp);
 
     return code;
 }       
@@ -6131,6 +6196,21 @@ smb_ReceiveCoreRename(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
              osi_LogSaveClientString(smb_logp, oldPathp),
              osi_LogSaveClientString(smb_logp, newPathp));
 
+    if (!cm_IsValidClientString(newPathp)) {
+#ifdef DEBUG
+        clientchar_t * hexp;
+
+        hexp = cm_GetRawCharsAlloc(newPathp, -1);
+        osi_Log1(smb_logp, "CoreRename rejecting invalid name. [%S]",
+                 osi_LogSaveClientString(smb_logp, hexp));
+        if (hexp)
+            free(hexp);
+#else
+        osi_Log0(smb_logp, "CoreRename rejecting invalid name");
+#endif
+        return CM_ERROR_BADNTFILENAME;
+    }
+
     code = smb_Rename(vcp,inp,oldPathp,newPathp,0);
 
     osi_Log1(smb_logp, "smb rename returns 0x%x", code);
@@ -6158,7 +6238,12 @@ int smb_RmdirProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper
         
     rockp = (smb_rmdirRock_t *) vrockp;
 
-    cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName));
+    if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
+        osi_Log1(smb_logp, "Skipping entry [%s]. Can't normalize FS string",
+                 osi_LogSaveString(smb_logp, dep->name));
+        return 0;
+    }
+
     if (rockp->flags & SMB_MASKFLAG_CASEFOLD)
         match = (cm_ClientStrCmpI(matchName, rockp->maskp) == 0);
     else
@@ -6195,6 +6280,7 @@ long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
     cm_req_t req;
 
     smb_InitReq(&req);
+    memset(&rock, 0, sizeof(rock));
 
     tp = smb_GetSMBData(inp, NULL);
     pathp = smb_ParseASCIIBlock(inp, tp, &tp, SMB_STRF_ANSIPATH);
@@ -6239,6 +6325,10 @@ long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
        
     rock.any = 0;
     rock.maskp = cm_ClientStringToNormStringAlloc(lastNamep, -1, NULL);
+    if (!rock.maskp) {
+        code = CM_ERROR_NOSUCHFILE;
+        goto done;
+    }
     rock.flags = ((cm_ClientStrChr(rock.maskp, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
 
     thyper.LowPart = 0;
@@ -6263,6 +6353,8 @@ long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
         for (entry = rock.matches; code == 0 && entry; entry = entry->nextp) {
             clientchar_t clientName[MAX_PATH];
 
+            /* We assume this will succeed because smb_RmdirProc()
+               successfully converted entry->name once above. */
             cm_FsStringToClientString(entry->name, -1, clientName, lengthof(clientName));
 
             osi_Log1(smb_logp, "Removing directory %s",
@@ -6277,17 +6369,21 @@ long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
         }
     }
 
+  done:
+    if (rock.matches)
     cm_DirEntryListFree(&rock.matches);
 
+    if (userp)
     cm_ReleaseUser(userp);
         
+    if (dscp)
     cm_ReleaseSCache(dscp);
 
     if (code == 0 && !rock.any)
         code = CM_ERROR_NOSUCHFILE;        
 
+    if (rock.maskp)
     free(rock.maskp);
-    rock.maskp = NULL;
 
     return code;
 }
@@ -6362,7 +6458,11 @@ int smb_FullNameProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
 
     vrockp = (struct smb_FullNameRock *)rockp;
 
-    cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName));
+    if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
+        osi_Log1(smb_logp, "Skipping entry [%s]. Can't normalize FS string",
+                 osi_LogSaveString(smb_logp, dep->name));
+        return 0;
+    }
 
     if (!cm_Is8Dot3(matchName)) {
         clientchar_t shortName[13];
@@ -7704,6 +7804,21 @@ long smb_ReceiveCoreCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     tp = smb_GetSMBData(inp, NULL);
     pathp = smb_ParseASCIIBlock(inp, tp, &tp, SMB_STRF_ANSIPATH);
 
+    if (!cm_IsValidClientString(pathp)) {
+#ifdef DEBUG
+        clientchar_t * hexp;
+
+        hexp = cm_GetRawCharsAlloc(pathp, -1);
+        osi_Log1(smb_logp, "CoreCreate rejecting invalid name. [%S]",
+                 osi_LogSaveClientString(smb_logp, hexp));
+        if (hexp)
+            free(hexp);
+#else
+        osi_Log0(smb_logp, "CoreCreate rejecting invalid name");
+#endif
+        return CM_ERROR_BADNTFILENAME;
+    }
+
     spacep = inp->spacep;
     smb_StripLastComponent(spacep->wdata, &lastNamep, pathp);
 
index 2ec4362..68d398d 100644 (file)
@@ -2361,6 +2361,22 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
         return 0;
     }
 
+    if (!cm_IsValidClientString(pathp)) {
+#ifdef DEBUG
+        clientchar_t * hexp;
+
+        hexp = cm_GetRawCharsAlloc(pathp, -1);
+        osi_Log1(smb_logp, "Tran2Open rejecting invalid name. [%S]",
+                 osi_LogSaveClientString(smb_logp, hexp));
+        if (hexp)
+            free(hexp);
+#else
+        osi_Log0(smb_logp, "Tran2Open rejecting invalid name");
+#endif
+        smb_FreeTran2Packet(outp);
+        return CM_ERROR_BADNTFILENAME;
+    }
+
 #ifdef DEBUG_VERBOSE
     {
         char *hexp, *asciip;
@@ -2800,7 +2816,11 @@ int cm_GetShortNameProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *vrockp,
 
     rockp = vrockp;
 
-    cm_FsStringToNormString(dep->name, -1, normName, sizeof(normName)/sizeof(clientchar_t));
+    if (cm_FsStringToNormString(dep->name, -1, normName, sizeof(normName)/sizeof(clientchar_t)) == 0) {
+        osi_Log1(smb_logp, "Skipping entry [%s]. Can't normalize FS string",
+                 osi_LogSaveString(smb_logp, dep->name));
+        return 0;
+    }
 
     /* compare both names and vnodes, though probably just comparing vnodes
      * would be safe enough.
@@ -5287,8 +5307,13 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
         if (dep->fid.vnode == 0) 
             goto nextEntry;             /* This entry is not in use */
 
-        cm_FsStringToClientString(dep->name, -1, cfileName, lengthof(cfileName));
-        cm_ClientStringToNormString(cfileName, -1, normName, lengthof(normName));
+        if (cm_FsStringToClientString(dep->name, -1, cfileName, lengthof(cfileName)) == 0 ||
+            cm_ClientStringToNormString(cfileName, -1, normName, lengthof(normName)) == 0) {
+
+            osi_Log1(smb_logp, "Skipping entry [%s].  Can't convert or normalize FS String",
+                     osi_LogSaveString(smb_logp, dep->name));
+            goto nextEntry;
+        }
 
         /* Need 8.3 name? */
         NeedShortName = 0;
@@ -5700,6 +5725,21 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         return 0;
     }
 
+    if (!cm_IsValidClientString(pathp)) {
+#ifdef DEBUG
+        clientchar_t * hexp;
+
+        hexp = cm_GetRawCharsAlloc(pathp, -1);
+        osi_Log1(smb_logp, "NTOpenX rejecting invalid name. [%S]",
+                 osi_LogSaveClientString(smb_logp, hexp));
+        if (hexp)
+            free(hexp);
+#else
+        osi_Log0(smb_logp, "NTOpenX rejecting invalid name");
+#endif
+        return CM_ERROR_BADNTFILENAME;
+    }
+
 #ifdef DEBUG_VERBOSE
     {
        char *hexp, *asciip;
@@ -6858,15 +6898,21 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         return 0;
     }
 
-#ifdef DEBUG_VERBOSE
-    {
-       char *hexp, *asciip;
-       asciip = (lastNamep? lastNamep : realPathp);
-       hexp = osi_HexifyString( asciip );
-       DEBUG_EVENT2("AFS", "NTCreateX H[%s] A[%s]", hexp, asciip);
+    if (!cm_IsValidClientString(realPathp)) {
+#ifdef DEBUG
+        clientchar_t * hexp;
+
+        hexp = cm_GetRawCharsAlloc(realPathp, -1);
+        osi_Log1(smb_logp, "NTCreateX rejecting invalid name. [%S]",
+                 osi_LogSaveClientString(smb_logp, hexp));
+        if (hexp)
        free(hexp);
-    }
+#else
+        osi_Log0(smb_logp, "NTCreateX rejecting invalid name");
 #endif
+        free(realPathp);
+        return CM_ERROR_BADNTFILENAME;
+    }
 
     userp = smb_GetUserFromVCP(vcp, inp);
     if (!userp) {
@@ -7679,15 +7725,21 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
      * Will add it if necessary.
      */
 
-#ifdef DEBUG_VERBOSE
-    {
-        char *hexp, *asciip;
-        asciip = (lastNamep? lastNamep : realPathp);
-        hexp = osi_HexifyString( asciip );
-        DEBUG_EVENT2("AFS", "NTTranCreate H[%s] A[%s]", hexp, asciip);
+    if (!cm_IsValidClientString(realPathp)) {
+#ifdef DEBUG
+        clientchar_t * hexp;
+
+        hexp = cm_GetRawCharsAlloc(realPathp, -1);
+        osi_Log1(smb_logp, "NTTranCreate rejecting invalid name. [%S]",
+                 osi_LogSaveClientString(smb_logp, hexp));
+        if (hexp)
         free(hexp);
-    }
+#else
+        osi_Log0(smb_logp, "NTTranCreate rejecting invalid name.");
 #endif
+        free(realPathp);
+        return CM_ERROR_BADNTFILENAME;
+    }
 
     userp = smb_GetUserFromVCP(vcp, inp);
     if (!userp) {