From 90e4242c2d7d4ce8477061469f4840212c195080 Mon Sep 17 00:00:00 2001 From: Asanka Herath Date: Mon, 20 Oct 2008 00:17:41 +0000 Subject: [PATCH] windows-handle-invalid-utf16-names-20081019 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 | 30 +++++- src/WINNT/afsd/cm_dir.c | 1 - src/WINNT/afsd/cm_ioctl.c | 12 +++ src/WINNT/afsd/cm_nls.c | 188 +++++++++++++++++++++++++++++++++++ src/WINNT/afsd/cm_nls.h | 20 ++++ src/WINNT/afsd/cm_vnodeops.c | 22 +++- src/WINNT/afsd/cm_volstat.c | 14 +++ src/WINNT/afsd/smb.c | 181 +++++++++++++++++++++++++++------ src/WINNT/afsd/smb3.c | 86 ++++++++++++---- 9 files changed, 499 insertions(+), 55 deletions(-) diff --git a/src/WINNT/afsd/cm_btree.c b/src/WINNT/afsd/cm_btree.c index c7bc2b7e75..ea0ec3abda 100644 --- a/src/WINNT/afsd/cm_btree.c +++ b/src/WINNT/afsd/cm_btree.c @@ -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) diff --git a/src/WINNT/afsd/cm_dir.c b/src/WINNT/afsd/cm_dir.c index edc4fc58f1..5f2439c1e3 100644 --- a/src/WINNT/afsd/cm_dir.c +++ b/src/WINNT/afsd/cm_dir.c @@ -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; } diff --git a/src/WINNT/afsd/cm_ioctl.c b/src/WINNT/afsd/cm_ioctl.c index 1d78f0c67d..c24ce06b23 100644 --- a/src/WINNT/afsd/cm_ioctl.c +++ b/src/WINNT/afsd/cm_ioctl.c @@ -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,8 +1460,12 @@ 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; diff --git a/src/WINNT/afsd/cm_nls.c b/src/WINNT/afsd/cm_nls.c index 8d3eb215ae..63e56e40d3 100644 --- a/src/WINNT/afsd/cm_nls.c +++ b/src/WINNT/afsd/cm_nls.c @@ -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, ¤t, &cb, 0, + L"%04x", (int) *c); + if (len > 1) + StringCbCatExW(current, cb, L",", ¤t, &cb, 0); + } + + return ret; +} +#endif diff --git a/src/WINNT/afsd/cm_nls.h b/src/WINNT/afsd/cm_nls.h index 017d2718ae..3d7517ea5d 100644 --- a/src/WINNT/afsd/cm_nls.h +++ b/src/WINNT/afsd/cm_nls.h @@ -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, diff --git a/src/WINNT/afsd/cm_vnodeops.c b/src/WINNT/afsd/cm_vnodeops.c index 82b57a8ced..e9d8d54d01 100644 --- a/src/WINNT/afsd/cm_vnodeops.c +++ b/src/WINNT/afsd/cm_vnodeops.c @@ -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) { diff --git a/src/WINNT/afsd/cm_volstat.c b/src/WINNT/afsd/cm_volstat.c index 3f1a2f6196..3336aae43b 100644 --- a/src/WINNT/afsd/cm_volstat.c +++ b/src/WINNT/afsd/cm_volstat.c @@ -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) diff --git a/src/WINNT/afsd/smb.c b/src/WINNT/afsd/smb.c index f66cc05c5f..d1d4ac980d 100644 --- a/src/WINNT/afsd/smb.c +++ b/src/WINNT/afsd/smb.c @@ -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); diff --git a/src/WINNT/afsd/smb3.c b/src/WINNT/afsd/smb3.c index 2ec4362b67..68d398d786 100644 --- a/src/WINNT/afsd/smb3.c +++ b/src/WINNT/afsd/smb3.c @@ -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) {