diff options
Diffstat (limited to 'cups')
-rw-r--r-- | cups/Makefile | 10 | ||||
-rw-r--r-- | cups/globals.c | 8 | ||||
-rw-r--r-- | cups/http-private.h | 3 | ||||
-rw-r--r-- | cups/http.c | 88 | ||||
-rw-r--r-- | cups/language-private.h | 2 | ||||
-rw-r--r-- | cups/language.c | 18 | ||||
-rwxr-xr-x | cups/libcups.order | 1 | ||||
-rw-r--r-- | cups/libcups2.def | 6 | ||||
-rw-r--r-- | cups/libcups_s.exp | 7 | ||||
-rw-r--r-- | cups/mark.c | 18 | ||||
-rw-r--r-- | cups/ppd-private.h | 4 | ||||
-rw-r--r-- | cups/pwg-file.c | 72 | ||||
-rw-r--r-- | cups/pwg-ppd.c | 135 | ||||
-rw-r--r-- | cups/pwg-private.h | 2 | ||||
-rw-r--r-- | cups/sspi-private.h | 82 | ||||
-rw-r--r-- | cups/sspi.c | 1472 | ||||
-rw-r--r-- | cups/testi18n.c | 66 | ||||
-rw-r--r-- | cups/testppd.c | 14 | ||||
-rw-r--r-- | cups/transcode.c | 313 |
19 files changed, 1997 insertions, 324 deletions
diff --git a/cups/Makefile b/cups/Makefile index cd557d57a..e3f6ec241 100644 --- a/cups/Makefile +++ b/cups/Makefile @@ -87,8 +87,7 @@ OBJS = \ testlang.o \ testppd.o \ testpwg.o \ - testsnmp.o \ - php_cups_wrap.o + testsnmp.o # @@ -355,15 +354,14 @@ libcups.so.2 libcups.sl.2: $(LIBOBJS) libcups.2.dylib: $(LIBOBJS) $(LIBCUPSORDER) echo Creating export list for $@... nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}' | \ - grep -v -e '^(_cupsConnect|_cupsSetDefaults|_cupsSetHTTPError|_cupsUserDefault|_httpWait)$$' | \ + grep -v -e '^(_cupsConnect|_cupsCharset|_cupsEncodingName|_cupsSetDefaults|_cupsSetHTTPError|_cupsUserDefault|_httpWait)$$' | \ sort >t.exp echo Linking $@... $(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ \ -install_name $(libdir)/$@ \ - -current_version 2.8.0 \ + -current_version 2.9.0 \ -compatibility_version 2.0.0 \ -exported_symbols_list t.exp \ - -sectorder __TEXT __text $(LIBCUPSORDER) \ $(LIBOBJS) $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) \ $(COMMONLIBS) $(LIBZ) $(RM) libcups.dylib t.exp @@ -390,7 +388,7 @@ libcups_s.a: $(LIBOBJS) libcups_s.exp libcups.la: $(LIBOBJS) echo Linking $@... $(CC) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(LIBOBJS:.o=.lo) \ - -rpath $(LIBDIR) -version-info 2:8 $(LIBGSSAPI) $(SSLLIBS) \ + -rpath $(LIBDIR) -version-info 2:9 $(LIBGSSAPI) $(SSLLIBS) \ $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ) diff --git a/cups/globals.c b/cups/globals.c index 00a73691c..04afcab2c 100644 --- a/cups/globals.c +++ b/cups/globals.c @@ -68,7 +68,7 @@ _cupsGlobalLock(void) #ifdef HAVE_PTHREAD_H pthread_mutex_lock(&cups_global_mutex); #elif defined(WIN32) - EnterCriticalSection(&cups_global_mutex->m_criticalSection); + EnterCriticalSection(&cups_global_mutex.m_criticalSection); #endif /* HAVE_PTHREAD_H */ } @@ -123,7 +123,7 @@ _cupsGlobalUnlock(void) #ifdef HAVE_PTHREAD_H pthread_mutex_unlock(&cups_global_mutex); #elif defined(WIN32) - LeaveCriticalSection(&cups_global_mutex->m_criticalSection); + LeaveCriticalSection(&cups_global_mutex.m_criticalSection); #endif /* HAVE_PTHREAD_H */ } @@ -147,7 +147,7 @@ DllMain(HINSTANCE hinst, /* I - DLL module handle */ switch (reason) { case DLL_PROCESS_ATTACH : /* Called on library initialization */ - InitializeCriticalSection(&cups_global_lock); + InitializeCriticalSection(&cups_global_mutex.m_criticalSection); if ((cups_globals_key = TlsAlloc()) == TLS_OUT_OF_INDEXES) return (FALSE); @@ -163,7 +163,7 @@ DllMain(HINSTANCE hinst, /* I - DLL module handle */ cups_globals_free(cg); TlsFree(cups_globals_key); - DeleteCriticalSection(&cups_global_lock); + DeleteCriticalSection(&cups_global_mutex.m_criticalSection); break; default: diff --git a/cups/http-private.h b/cups/http-private.h index 50f094cba..78ebdf930 100644 --- a/cups/http-private.h +++ b/cups/http-private.h @@ -130,6 +130,9 @@ extern OSStatus _httpReadCDSA(SSLConnectionRef connection, void *data, size_t *dataLength); extern OSStatus _httpWriteCDSA(SSLConnectionRef connection, const void *data, size_t *dataLength); + +# elif defined(HAVE_SSPISSL) +# include "sspi-private.h" # endif /* HAVE_LIBSSL */ diff --git a/cups/http.c b/cups/http.c index 666c6458c..a83fc20cc 100644 --- a/cups/http.c +++ b/cups/http.c @@ -103,11 +103,13 @@ #include "cups-private.h" #include <fcntl.h> -#ifndef WIN32 +#ifdef WIN32 +# include <tchar.h> +#else # include <signal.h> # include <sys/time.h> # include <sys/resource.h> -#endif /* !WIN32 */ +#endif /* WIN32 */ #ifdef HAVE_POLL # include <sys/poll.h> #endif /* HAVE_POLL */ @@ -1116,13 +1118,16 @@ httpGets(char *line, /* I - Line to read into */ */ #ifdef WIN32 - if (WSAGetLastError() != http->error) + DEBUG_printf(("3httpGets: recv() error %d!", WSAGetLastError())); + + if (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) + continue; + else if (WSAGetLastError() != http->error) { http->error = WSAGetLastError(); continue; } - DEBUG_printf(("3httpGets: recv() error %d!", WSAGetLastError())); #else DEBUG_printf(("3httpGets: recv() error %d!", errno)); @@ -1437,8 +1442,11 @@ _httpPeek(http_t *http, /* I - Connection to server */ else if (bytes < 0) { #ifdef WIN32 - http->error = WSAGetLastError(); - return (-1); + if (WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK) + { + http->error = WSAGetLastError(); + return (-1); + } #else if (errno != EINTR && errno != EAGAIN) { @@ -1472,7 +1480,10 @@ _httpPeek(http_t *http, /* I - Connection to server */ if (bytes < 0) { #ifdef WIN32 - http->error = WSAGetLastError(); + if (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) + bytes = 0; + else + http->error = WSAGetLastError(); #else if (errno == EINTR || errno == EAGAIN) bytes = 0; @@ -1687,8 +1698,11 @@ httpRead2(http_t *http, /* I - Connection to server */ else if (bytes < 0) { #ifdef WIN32 - http->error = WSAGetLastError(); - return (-1); + if (WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK) + { + http->error = WSAGetLastError(); + return (-1); + } #else if (errno != EINTR && errno != EAGAIN) { @@ -1738,7 +1752,9 @@ httpRead2(http_t *http, /* I - Connection to server */ CUPS_LLCAST length)); #ifdef WIN32 - bytes = (ssize_t)recv(http->fd, buffer, (int)length, 0); + while ((bytes = (ssize_t) recv(http->fd, buffer, (int)length, 0)) < 0) + if (WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK) + break; #else while ((bytes = recv(http->fd, buffer, length, 0)) < 0) if (errno != EINTR && errno != EAGAIN) @@ -1761,7 +1777,10 @@ httpRead2(http_t *http, /* I - Connection to server */ else if (bytes < 0) { #ifdef WIN32 - http->error = WSAGetLastError(); + if (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) + bytes = 0; + else + http->error = WSAGetLastError(); #else if (errno == EINTR || errno == EAGAIN) bytes = 0; @@ -2489,7 +2508,8 @@ _httpWait(http_t *http, /* I - Connection to server */ DEBUG_printf(("6_httpWait: select() returned %d...", nfds)); } # ifdef WIN32 - while (nfds < 0 && WSAGetLastError() == WSAEINTR); + while (nfds < 0 && (WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEWOULDBLOCK)); # else while (nfds < 0 && (errno == EINTR || errno == EAGAIN)); # endif /* WIN32 */ @@ -3047,6 +3067,8 @@ http_read_ssl(http_t *http, /* I - Connection to server */ } return (result); +# elif defined(HAVE_SSPISSL) + return _sspiRead((_sspi_struct_t*) http->tls, buf, len); # endif /* HAVE_LIBSSL */ } #endif /* HAVE_SSL */ @@ -3244,6 +3266,11 @@ http_setup_ssl(http_t *http) /* I - Connection to server */ # elif defined(HAVE_CDSASSL) OSStatus error; /* Error code */ http_tls_t *conn; /* CDSA connection information */ +# elif defined(HAVE_SSPISSL) + TCHAR username[256]; /* Username returned from GetUserName() */ + TCHAR commonName[256]; /* Common name for certificate */ + DWORD dwSize; /* 32 bit size */ + _sspi_struct_t *conn; /* SSPI connection information */ # endif /* HAVE_LIBSSL */ @@ -3377,6 +3404,32 @@ http_setup_ssl(http_t *http) /* I - Connection to server */ return (-1); } +# elif defined(HAVE_SSPISSL) + conn = _sspiAlloc(); + + if (!conn) + return (-1); + + conn->sock = http->fd; + dwSize = sizeof(username) / sizeof(TCHAR); + GetUserName(username, &dwSize); + _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR), + sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username); + + if (!_sspiGetCredentials(conn, L"ClientContainer", commonName, FALSE)) + { + _sspiFree(conn); + return (-1); + } + + _sspiSetAllowsAnyRoot(conn, TRUE); + _sspiSetAllowsExpiredCerts(conn, TRUE); + + if (!_sspiConnect(conn, http->hostname)) + { + _sspiFree(conn); + return (-1); + } # endif /* HAVE_CDSASSL */ http->tls = conn; @@ -3435,6 +3488,11 @@ http_shutdown_ssl(http_t *http) /* I - Connection to server */ CFRelease(conn->certsArray); free(conn); +# elif defined(HAVE_SSPISSL) + _sspi_struct_t *conn; /* SSPI connection information */ + + conn = (_sspi_struct_t*)(http->tls); + _sspiFree(conn); # endif /* HAVE_LIBSSL */ http->tls = NULL; @@ -3577,7 +3635,9 @@ http_write(http_t *http, /* I - Connection to server */ if (bytes < 0) { #ifdef WIN32 - if (WSAGetLastError() != http->error) + if (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) + continue; + else if (WSAGetLastError() != http->error && WSAGetLastError() != WSAECONNRESET) { http->error = WSAGetLastError(); continue; @@ -3730,6 +3790,8 @@ http_write_ssl(http_t *http, /* I - Connection to server */ result = -1; break; } +# elif defined(HAVE_SSPISSL) + return _sspiWrite((_sspi_struct_t*) http->tls, (void*) buf, len); # endif /* HAVE_LIBSSL */ DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result)); diff --git a/cups/language-private.h b/cups/language-private.h index 1defb8c22..934032eee 100644 --- a/cups/language-private.h +++ b/cups/language-private.h @@ -87,8 +87,6 @@ extern const char *_cupsAppleLanguage(const char *locale, char *language, size_t langsize); # endif /* __APPLE__ */ extern void _cupsCharmapFlush(void); -extern void _cupsCharmapFree(const cups_encoding_t encoding); -extern void *_cupsCharmapGet(const cups_encoding_t encoding); extern const char *_cupsEncodingName(cups_encoding_t encoding); extern void _cupsLangPrintError(const char *message); extern int _cupsLangPrintf(FILE *fp, const char *message, ...) diff --git a/cups/language.c b/cups/language.c index 00b2de3ad..8ce4bc7d1 100644 --- a/cups/language.c +++ b/cups/language.c @@ -71,12 +71,12 @@ static const char * const lang_encodings[] = "iso-8859-8", "iso-8859-9", "iso-8859-10", "utf-8", "iso-8859-13", "iso-8859-14", - "iso-8859-15", "windows-874", - "windows-1250", "windows-1251", - "windows-1252", "windows-1253", - "windows-1254", "windows-1255", - "windows-1256", "windows-1257", - "windows-1258", "koi8-r", + "iso-8859-15", "cp874", + "cp1250", "cp1251", + "cp1252", "cp1253", + "cp1254", "cp1255", + "cp1256", "cp1257", + "cp1258", "koi8-r", "koi8-u", "iso-8859-11", "iso-8859-16", "mac-roman", "unknown", "unknown", @@ -96,9 +96,9 @@ static const char * const lang_encodings[] = "unknown", "unknown", "unknown", "unknown", "unknown", "unknown", - "windows-932", "windows-936", - "windows-949", "windows-950", - "windows-1361", "unknown", + "cp932", "cp936", + "cp949", "cp950", + "cp1361", "unknown", "unknown", "unknown", "unknown", "unknown", "unknown", "unknown", diff --git a/cups/libcups.order b/cups/libcups.order index 92820f7c2..e69de29bb 100755 --- a/cups/libcups.order +++ b/cups/libcups.order @@ -1 +0,0 @@ -single module:dyld_stub_binding_helper diff --git a/cups/libcups2.def b/cups/libcups2.def index 825d12e68..722a8ad95 100644 --- a/cups/libcups2.def +++ b/cups/libcups2.def @@ -1,10 +1,6 @@ LIBRARY libcups2
VERSION 2.8
EXPORTS
-_cupsCharmapFlush
-_cupsCharmapFree
-_cupsCharmapGet
-_cupsEncodingName
_cupsGetPassword
_cupsGlobals
_cupsLangPrintf
@@ -46,8 +42,10 @@ _pwgMediaForPPD _pwgMediaForPWG
_pwgMediaForSize
_pwgCreateWithPPD
+_pwgGetBin
_pwgGetInputSlot
_pwgGetMediaType
+_pwgGetOutputBin
_pwgGetPageSize
_pwgGetSize
_pwgGetSource
diff --git a/cups/libcups_s.exp b/cups/libcups_s.exp index 35037ea5b..db57eea66 100644 --- a/cups/libcups_s.exp +++ b/cups/libcups_s.exp @@ -1,11 +1,8 @@ _cups_debug_fd -_cupsCharmapFlush -_cupsCharmapFree -_cupsCharmapGet -_cupsEncodingName _cupsGet1284Values _cupsGetPassword _cupsGlobals +_cupsLangPrintError _cupsLangPrintf _cupsLangPuts _cupsLangString @@ -67,8 +64,10 @@ _pwgMediaForPPD _pwgMediaForPWG _pwgMediaForSize _pwgCreateWithPPD +_pwgGetBin _pwgGetInputSlot _pwgGetMediaType +_pwgGetOutputBin _pwgGetPageSize _pwgGetSize _pwgGetSource diff --git a/cups/mark.c b/cups/mark.c index 9ef999b7f..e75544b72 100644 --- a/cups/mark.c +++ b/cups/mark.c @@ -161,10 +161,14 @@ cupsMarkOptions( * Mark it... */ - if ((!page_size || !page_size[0]) && - (ppd_keyword = _pwgGetPageSize((_pwg_t *)ppd->pwg, NULL, s, - NULL)) != NULL) - ppd_mark_option(ppd, "PageSize", ppd_keyword); + if (!page_size || !page_size[0]) + { + if (!strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s)) + ppd_mark_option(ppd, "PageSize", s); + else if ((ppd_keyword = _pwgGetPageSize((_pwg_t *)ppd->pwg, NULL, s, + NULL)) != NULL) + ppd_mark_option(ppd, "PageSize", ppd_keyword); + } if (!cupsGetOption("InputSlot", num_options, options) && (ppd_keyword = _pwgGetInputSlot((_pwg_t *)ppd->pwg, NULL, s)) != NULL) @@ -286,8 +290,10 @@ cupsMarkOptions( } else if (!strcasecmp(optptr->name, "output-bin")) { - if (!cupsGetOption("OutputBin", num_options, options)) - ppd_mark_option(ppd, "OutputBin", optptr->value); + if (!cupsGetOption("OutputBin", num_options, options) && + (ppd_keyword = _pwgGetOutputBin((_pwg_t *)ppd->pwg, + optptr->value)) != NULL) + ppd_mark_option(ppd, "OutputBin", ppd_keyword); } else if (!strcasecmp(optptr->name, "multiple-document-handling")) { diff --git a/cups/ppd-private.h b/cups/ppd-private.h index f7ade9407..2867fc83e 100644 --- a/cups/ppd-private.h +++ b/cups/ppd-private.h @@ -80,10 +80,12 @@ extern char *_ppdNormalizeMakeAndModel(const char *make_and_model, extern int _ppdParseOptions(const char *s, int num_options, cups_option_t **options); extern _pwg_t *_pwgCreateWithPPD(ppd_file_t *ppd); +extern const char *_pwgGetBin(_pwg_t *pwg, const char *output_bin); extern const char *_pwgGetInputSlot(_pwg_t *pwg, ipp_t *job, const char *keyword); extern const char *_pwgGetMediaType(_pwg_t *pwg, ipp_t *job, const char *keyword); +extern const char *_pwgGetOutputBin(_pwg_t *pwg, const char *keyword); extern const char *_pwgGetPageSize(_pwg_t *pwg, ipp_t *job, const char *keyword, int *exact); extern _pwg_size_t *_pwgGetSize(_pwg_t *pwg, const char *page_size); @@ -92,7 +94,7 @@ extern const char *_pwgGetType(_pwg_t *pwg, const char *media_type); extern const char *_pwgInputSlotForSource(const char *media_source, char *name, size_t namesize); extern _pwg_media_t *_pwgMediaForPPD(const char *ppd); -extern const char *_pwgMediaTypeForType(const char *media_source, +extern const char *_pwgMediaTypeForType(const char *media_type, char *name, size_t namesize); extern const char *_pwgPageSizeForMedia(_pwg_media_t *media, char *name, size_t namesize); diff --git a/cups/pwg-file.c b/cups/pwg-file.c index b81339289..8e4a4835e 100644 --- a/cups/pwg-file.c +++ b/cups/pwg-file.c @@ -42,6 +42,7 @@ _pwgCreateWithFile(const char *filename)/* I - File to read */ _pwg_size_t *size; /* Current size */ _pwg_map_t *map; /* Current map */ int linenum, /* Current line number */ + num_bins, /* Number of bins in file */ num_sizes, /* Number of sizes in file */ num_sources, /* Number of sources in file */ num_types; /* Number of types in file */ @@ -90,6 +91,7 @@ _pwgCreateWithFile(const char *filename)/* I - File to read */ */ linenum = 0; + num_bins = 0; num_sizes = 0; num_sources = 0; num_types = 0; @@ -105,6 +107,54 @@ _pwgCreateWithFile(const char *filename)/* I - File to read */ _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PWG mapping file."), 1); goto create_error; } + else if (!strcasecmp(line, "NumBins")) + { + if (num_bins > 0) + { + DEBUG_puts("_pwgCreateWithFile: NumBins listed multiple times."); + _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PWG mapping file."), 1); + goto create_error; + } + + if ((num_bins = atoi(value)) <= 0 || num_bins > 65536) + { + DEBUG_printf(("_pwgCreateWithFile: Bad NumBins value %d on line %d.", + num_sizes, linenum)); + _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PWG mapping file."), 1); + goto create_error; + } + + if ((pwg->bins = calloc(num_bins, sizeof(_pwg_map_t))) == NULL) + { + DEBUG_printf(("_pwgCreateWithFile: Unable to allocate %d bins.", + num_sizes)); + _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0); + goto create_error; + } + } + else if (!strcasecmp(line, "Bin")) + { + if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2) + { + DEBUG_printf(("_pwgCreateWithFile: Bad Bin on line %d.", linenum)); + _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PWG mapping file."), 1); + goto create_error; + } + + if (pwg->num_bins >= num_bins) + { + DEBUG_printf(("_pwgCreateWithFile: Too many Bin's on line %d.", + linenum)); + _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PWG mapping file."), 1); + goto create_error; + } + + map = pwg->bins + pwg->num_bins; + map->pwg = _cupsStrAlloc(pwg_keyword); + map->ppd = _cupsStrAlloc(ppd_keyword); + + pwg->num_bins ++; + } else if (!strcasecmp(line, "NumSizes")) { if (num_sizes > 0) @@ -354,6 +404,17 @@ _pwgDestroy(_pwg_t *pwg) /* I - PWG mapping data */ * Free memory as needed... */ + if (pwg->bins) + { + for (i = pwg->num_bins, map = pwg->bins; i > 0; i --, map ++) + { + _cupsStrFree(map->pwg); + _cupsStrFree(map->ppd); + } + + free(pwg->bins); + } + if (pwg->sizes) { for (i = pwg->num_sizes, size = pwg->sizes; i > 0; i --, size ++) @@ -438,6 +499,17 @@ _pwgWriteFile(_pwg_t *pwg, /* I - PWG mapping data */ cupsFilePuts(fp, "#CUPS-PWGPPD\n"); /* + * Output bins... + */ + + if (pwg->num_bins > 0) + { + cupsFilePrintf(fp, "NumBins %d\n", pwg->num_bins); + for (i = pwg->num_bins, map = pwg->bins; i > 0; i --, map ++) + cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd); + } + + /* * Media sizes... */ diff --git a/cups/pwg-ppd.c b/cups/pwg-ppd.c index ca3d88fb8..714304483 100644 --- a/cups/pwg-ppd.c +++ b/cups/pwg-ppd.c @@ -16,10 +16,14 @@ * Contents: * * _pwgCreateWithPPD() - Create PWG mapping data from a PPD file. + * _pwgGetBin() - Get the PWG output-bin keyword associated with a + * PPD OutputBin. * _pwgGetInputSlot() - Get the PPD InputSlot associated with the job * attributes or a keyword string. * _pwgGetMediaType() - Get the PPD MediaType associated with the job * attributes or a keyword string. + * _pwgGetOutputBin() - Get the PPD OutputBin associated with the + * keyword string. * _pwgGetPageSize() - Get the PPD PageSize associated with the job * attributes or a keyword string. * _pwgGetSize() - Get the PWG size associated with a PPD PageSize. @@ -61,7 +65,8 @@ _pwgCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ int i, j; /* Looping vars */ _pwg_t *pwg; /* PWG mapping data */ ppd_option_t *input_slot, /* InputSlot option */ - *media_type; /* MediaType option */ + *media_type, /* MediaType option */ + *output_bin; /* OutputBin option */ ppd_choice_t *choice; /* Current InputSlot/MediaType */ _pwg_map_t *map; /* Current source/type map */ ppd_size_t *ppd_size; /* Current PPD size */ @@ -327,6 +332,35 @@ _pwgCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ } } + + /* + * Copy and convert OutputBin data... + */ + + if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL) + { + if ((pwg->bins = calloc(output_bin->num_choices, + sizeof(_pwg_map_t))) == NULL) + { + DEBUG_printf(("_pwgCreateWithPPD: Unable to allocate %d _pwg_map_t's " + "for OutputBin.", output_bin->num_choices)); + goto create_error; + } + + pwg->num_bins = output_bin->num_choices; + + for (i = output_bin->num_choices, choice = output_bin->choices, + map = pwg->bins; + i > 0; + i --, choice ++, map ++) + { + pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword)); + + map->pwg = _cupsStrAlloc(pwg_keyword); + map->ppd = _cupsStrAlloc(choice->choice); + } + } + return (pwg); /* @@ -343,6 +377,38 @@ _pwgCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ /* + * '_pwgGetBin()' - Get the PWG output-bin keyword associated with a PPD + * OutputBin. + */ + +const char * /* O - output-bin or NULL */ +_pwgGetBin(_pwg_t *pwg, /* I - PWG mapping data */ + const char *output_bin) /* I - PPD OutputBin string */ +{ + int i; /* Looping var */ + + + /* + * Range check input... + */ + + if (!pwg || !output_bin) + return (NULL); + + /* + * Look up the OutputBin string... + */ + + + for (i = 0; i < pwg->num_bins; i ++) + if (!strcasecmp(output_bin, pwg->bins[i].ppd)) + return (pwg->bins[i].pwg); + + return (NULL); +} + + +/* * '_pwgGetInputSlot()' - Get the PPD InputSlot associated with the job * attributes or a keyword string. */ @@ -443,6 +509,38 @@ _pwgGetMediaType(_pwg_t *pwg, /* I - PWG mapping data */ /* + * '_pwgGetOutputBin()' - Get the PPD OutputBin associated with the keyword + * string. + */ + +const char * /* O - PPD OutputBin or NULL */ +_pwgGetOutputBin(_pwg_t *pwg, /* I - PWG mapping data */ + const char *output_bin)/* I - Keyword string */ +{ + int i; /* Looping var */ + + + /* + * Range check input... + */ + + if (!pwg || !output_bin) + return (NULL); + + /* + * Look up the OutputBin string... + */ + + + for (i = 0; i < pwg->num_bins; i ++) + if (!strcasecmp(output_bin, pwg->bins[i].pwg)) + return (pwg->bins[i].ppd); + + return (NULL); +} + + +/* * '_pwgGetPageSize()' - Get the PPD PageSize associated with the job * attributes or a keyword string. */ @@ -619,6 +717,13 @@ _pwgGetSize(_pwg_t *pwg, /* I - PWG mapping data */ _pwg_size_t *size; /* Current size */ + /* + * Range check input... + */ + + if (!pwg || !page_size) + return (NULL); + if (!strncasecmp(page_size, "Custom.", 7)) { /* @@ -706,6 +811,13 @@ _pwgGetSource(_pwg_t *pwg, /* I - PWG mapping data */ _pwg_map_t *source; /* Current source */ + /* + * Range check input... + */ + + if (!pwg || !input_slot) + return (NULL); + for (i = pwg->num_sources, source = pwg->sources; i > 0; i --, source ++) if (!strcasecmp(input_slot, source->ppd)) return (source->pwg); @@ -726,6 +838,13 @@ _pwgGetType(_pwg_t *pwg, /* I - PWG mapping data */ _pwg_map_t *type; /* Current type */ + /* + * Range check input... + */ + + if (!pwg || !media_type) + return (NULL); + for (i = pwg->num_types, type = pwg->types; i > 0; i --, type ++) if (!strcasecmp(media_type, type->ppd)) return (type->pwg); @@ -744,6 +863,13 @@ _pwgInputSlotForSource( char *name, /* I - Name buffer */ size_t namesize) /* I - Size of name buffer */ { + /* + * Range check input... + */ + + if (!media_source || !name || namesize < PPD_MAX_NAME) + return (NULL); + if (strcasecmp(media_source, "main")) strlcpy(name, "Cassette", namesize); else if (strcasecmp(media_source, "alternate")) @@ -783,6 +909,13 @@ _pwgMediaTypeForType( char *name, /* I - Name buffer */ size_t namesize) /* I - Size of name buffer */ { + /* + * Range check input... + */ + + if (!media_type || !name || namesize < PPD_MAX_NAME) + return (NULL); + if (strcasecmp(media_type, "auto")) strlcpy(name, "Auto", namesize); else if (strcasecmp(media_type, "cardstock")) diff --git a/cups/pwg-private.h b/cups/pwg-private.h index fa266a2bf..623fc5801 100644 --- a/cups/pwg-private.h +++ b/cups/pwg-private.h @@ -80,6 +80,8 @@ typedef struct _pwg_size_s /**** Size element - PPD to/from PWG */ typedef struct _pwg_s /**** PWG-PPD conversion data ****/ { + int num_bins; /* Number of output bins */ + _pwg_map_t *bins; /* Output bins */ int num_sizes; /* Number of media sizes */ _pwg_size_t *sizes; /* Media sizes */ int custom_max_width, /* Maximum custom width in 2540ths */ diff --git a/cups/sspi-private.h b/cups/sspi-private.h new file mode 100644 index 000000000..e8f36c2d1 --- /dev/null +++ b/cups/sspi-private.h @@ -0,0 +1,82 @@ +/* + * Private SSPI definitions for CUPS. + * + * Copyright 2010 by Apple Inc. + * + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". + */ + +#ifndef _CUPS_SSPI_PRIVATE_H_ +# define _CUPS_SSPI_PRIVATE_H_ + +/* + * Include necessary headers... + */ + +# include <config.h> +# include <winsock2.h> +# include <ws2tcpip.h> +# include <wincrypt.h> +# include <wintrust.h> +# include <schannel.h> +# define SECURITY_WIN32 +# include <security.h> +# include <sspi.h> + +/* + * C++ magic... + */ + +# ifdef __cplusplus +extern "C" { +# endif /* __cplusplus */ + + +typedef struct /**** SSPI/SSL data structure ****/ +{ + SOCKET sock; /* TCP/IP socket */ + CredHandle creds; /* Credentials */ + CtxtHandle context; /* SSL context */ + BOOL contextInitialized; /* Is context init'd? */ + SecPkgContext_StreamSizes streamSizes; /* SSL data stream sizes */ + BYTE *decryptBuffer; /* Data pre-decryption*/ + size_t decryptBufferLength; /* Length of decrypt buffer */ + size_t decryptBufferUsed; /* Bytes used in buffer */ + BYTE *readBuffer; /* Data post-decryption */ + size_t readBufferLength; /* Length of read buffer */ + size_t readBufferUsed; /* Bytes used in buffer */ + DWORD certFlags; /* Cert verification flags */ +} _sspi_struct_t; + + +/* + * Prototypes... + */ +_sspi_struct_t *_sspiAlloc(void); +BOOL _sspiAccept(_sspi_struct_t *conn); +BOOL _sspiConnect(_sspi_struct_t *conn, + const CHAR *hostname); +void _sspiFree(_sspi_struct_t *conn); +BOOL _sspiGetCredentials(_sspi_struct_t *conn, + const LPWSTR containerName, + const TCHAR *commonName, + BOOL server); +int _sspiPending(_sspi_struct_t *conn); +int _sspiRead(_sspi_struct_t *conn, + void *buf, size_t len); +void _sspiSetAllowsAnyRoot(_sspi_struct_t *conn, + BOOL allow); +void _sspiSetAllowsExpiredCerts(_sspi_struct_t *conn, + BOOL allow); +int _sspiWrite(_sspi_struct_t *conn, + void *buf, size_t len); + + +# ifdef __cplusplus +} +# endif /* __cplusplus */ +#endif /* !_CUPS_SSPI_PRIVATE_H_ */ diff --git a/cups/sspi.c b/cups/sspi.c new file mode 100644 index 000000000..7f8a07300 --- /dev/null +++ b/cups/sspi.c @@ -0,0 +1,1472 @@ +/* + * Windows SSPI SSL implementation for the Common UNIX Printing System (CUPS). + * + * Copyright 2010 by Apple Inc. + * + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". + * + * Contents: + * + * _sspiAlloc() - Allocate and initialize SSPI/SSL data structure + * _sspiAccept() - Accept a new SSL connection + * _sspiConnect() - Connect an SSL connection + * _sspiFree() - Close and deallocate SSPI/SSL connection + * _sspiGetCredentials() - Get credentials associated with connection + * _sspiPending() - Return number of bytes available to read + * _sspiRead() - Read a buffer of bytes + * _sspiSetAllowsAnyRoot() - Set client policy for untrusted root certs + * _sspiSetAllowsExpiredCerts() - Set client policy for expired certs + * _sspiWrite() - Write a buffer of bytes + * sspi_verify_certificate() - Verify a server certificate + */ + +/* + * Include necessary headers... + */ + +#include "sspi-private.h" +#include "debug-private.h" + + +/* required to link this library for certificate functions */ +#pragma comment(lib, "Crypt32.lib") +#pragma comment(lib, "Secur32.lib") +#pragma comment(lib, "Ws2_32.lib") + + +#if !defined(SECURITY_FLAG_IGNORE_UNKNOWN_CA) +# define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */ +#endif + +#if !defined(SECURITY_FLAG_IGNORE_CERT_DATE_INVALID) +# define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */ +#endif + +static DWORD sspi_verify_certificate(PCCERT_CONTEXT serverCert, + const CHAR *serverName, + DWORD dwCertFlags); + + +/* + * 'sspi_alloc()' - Allocate SSPI ssl object + */ +_sspi_struct_t* /* O - New SSPI/SSL object */ +_sspiAlloc(void) +{ + _sspi_struct_t *conn = calloc(sizeof(_sspi_struct_t), 1); + + if (conn) + conn->sock = INVALID_SOCKET; + + return (conn); +} + + +/* + * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store + * If one cannot be found, one is created. + */ +BOOL /* O - 1 on success, 0 on failure */ +_sspiGetCredentials(_sspi_struct_t *conn, + /* I - Client connection */ + const LPWSTR container, + /* I - Cert container name */ + const TCHAR *cn, /* I - Common name of certificate */ + BOOL isServer) + /* I - Is caller a server? */ +{ + HCERTSTORE store = NULL; /* Certificate store */ + PCCERT_CONTEXT storedContext = NULL; + /* Context created from the store */ + PCCERT_CONTEXT createdContext = NULL; + /* Context created by us */ + DWORD dwSize = 0; /* 32 bit size */ + PBYTE p = NULL; /* Temporary storage */ + HCRYPTPROV hProv = (HCRYPTPROV) NULL; + /* Handle to a CSP */ + CERT_NAME_BLOB sib; /* Arbitrary array of bytes */ + SCHANNEL_CRED SchannelCred; /* Schannel credential data */ + TimeStamp tsExpiry; /* Time stamp */ + SECURITY_STATUS Status; /* Status */ + HCRYPTKEY hKey = (HCRYPTKEY) NULL; + /* Handle to crypto key */ + CRYPT_KEY_PROV_INFO kpi; /* Key container info */ + SYSTEMTIME et; /* System time */ + CERT_EXTENSIONS exts; /* Array of cert extensions */ + CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */ + BOOL ok = TRUE; /* Return value */ + + if (!conn) + return (FALSE); + if (!cn) + return (FALSE); + + if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W, + PROV_RSA_FULL, + CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) + { + if (GetLastError() == NTE_EXISTS) + { + if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W, + PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)) + { + DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n", + GetLastError())); + ok = FALSE; + goto cleanup; + } + } + } + + store = CertOpenStore(CERT_STORE_PROV_SYSTEM, + X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, + hProv, + CERT_SYSTEM_STORE_LOCAL_MACHINE | + CERT_STORE_NO_CRYPT_RELEASE_FLAG | + CERT_STORE_OPEN_EXISTING_FLAG, + L"MY"); + + if (!store) + { + DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n", + GetLastError())); + ok = FALSE; + goto cleanup; + } + + dwSize = 0; + + if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR, + NULL, NULL, &dwSize, NULL)) + { + DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n", + GetLastError())); + ok = FALSE; + goto cleanup; + } + + p = (PBYTE) malloc(dwSize); + + if (!p) + { + DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize)); + ok = FALSE; + goto cleanup; + } + + if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR, NULL, + p, &dwSize, NULL)) + { + DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x", + GetLastError())); + ok = FALSE; + goto cleanup; + } + + sib.cbData = dwSize; + sib.pbData = p; + + storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, + 0, CERT_FIND_SUBJECT_NAME, &sib, NULL); + + if (!storedContext) + { + /* + * If we couldn't find the context, then we'll + * create a new one + */ + if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey)) + { + DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x", + GetLastError())); + ok = FALSE; + goto cleanup; + } + + ZeroMemory(&kpi, sizeof(kpi)); + kpi.pwszContainerName = (LPWSTR) container; + kpi.pwszProvName = MS_DEF_PROV_W; + kpi.dwProvType = PROV_RSA_FULL; + kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID; + kpi.dwKeySpec = AT_KEYEXCHANGE; + + GetSystemTime(&et); + et.wYear += 10; + + ZeroMemory(&exts, sizeof(exts)); + + createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL, + &et, &exts); + + if (!createdContext) + { + DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x", + GetLastError())); + ok = FALSE; + goto cleanup; + } + + if (!CertAddCertificateContextToStore(store, createdContext, + CERT_STORE_ADD_REPLACE_EXISTING, + &storedContext)) + { + DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x", + GetLastError())); + ok = FALSE; + goto cleanup; + } + + ZeroMemory(&ckp, sizeof(ckp)); + ckp.pwszContainerName = (LPWSTR) container; + ckp.pwszProvName = MS_DEF_PROV_W; + ckp.dwProvType = PROV_RSA_FULL; + ckp.dwFlags = CRYPT_MACHINE_KEYSET; + ckp.dwKeySpec = AT_KEYEXCHANGE; + + if (!CertSetCertificateContextProperty(storedContext, + CERT_KEY_PROV_INFO_PROP_ID, + 0, &ckp)) + { + DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x", + GetLastError())); + ok = FALSE; + goto cleanup; + } + } + + ZeroMemory(&SchannelCred, sizeof(SchannelCred)); + + SchannelCred.dwVersion = SCHANNEL_CRED_VERSION; + SchannelCred.cCreds = 1; + SchannelCred.paCred = &storedContext; + + /* + * SSPI doesn't seem to like it if grbitEnabledProtocols + * is set for a client + */ + if (isServer) + SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1; + + /* + * Create an SSPI credential. + */ + Status = AcquireCredentialsHandle(NULL, UNISP_NAME, + isServer ? SECPKG_CRED_INBOUND:SECPKG_CRED_OUTBOUND, + NULL, &SchannelCred, NULL, NULL, &conn->creds, + &tsExpiry); + if (Status != SEC_E_OK) + { + DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status)); + ok = FALSE; + goto cleanup; + } + +cleanup: + + /* + * Cleanup + */ + if (hKey) + CryptDestroyKey(hKey); + + if (createdContext) + CertFreeCertificateContext(createdContext); + + if (storedContext) + CertFreeCertificateContext(storedContext); + + if (p) + free(p); + + if (store) + CertCloseStore(store, 0); + + if (hProv) + CryptReleaseContext(hProv, 0); + + return (ok); +} + + +/* + * '_sspiConnect()' - Make an SSL connection. This function + * assumes a TCP/IP connection has already + * been successfully made + */ +BOOL /* O - 1 on success, 0 on failure */ +_sspiConnect(_sspi_struct_t *conn, /* I - Client connection */ + const CHAR *hostname) /* I - Server hostname */ +{ + PCCERT_CONTEXT serverCert; /* Server certificate */ + DWORD dwSSPIFlags; /* SSL connection attributes we want */ + DWORD dwSSPIOutFlags; /* SSL connection attributes we got */ + TimeStamp tsExpiry; /* Time stamp */ + SECURITY_STATUS scRet; /* Status */ + DWORD cbData; /* Data count */ + SecBufferDesc inBuffer; /* Array of SecBuffer structs */ + SecBuffer inBuffers[2]; /* Security package buffer */ + SecBufferDesc outBuffer; /* Array of SecBuffer structs */ + SecBuffer outBuffers[1]; /* Security package buffer */ + BOOL ok = TRUE; /* Return value */ + + serverCert = NULL; + + dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM; + + /* + * Initiate a ClientHello message and generate a token. + */ + outBuffers[0].pvBuffer = NULL; + outBuffers[0].BufferType = SECBUFFER_TOKEN; + outBuffers[0].cbBuffer = 0; + + outBuffer.cBuffers = 1; + outBuffer.pBuffers = outBuffers; + outBuffer.ulVersion = SECBUFFER_VERSION; + + scRet = InitializeSecurityContext(&conn->creds, NULL, TEXT(""), dwSSPIFlags, + 0, SECURITY_NATIVE_DREP, NULL, 0, &conn->context, + &outBuffer, &dwSSPIOutFlags, &tsExpiry); + + if (scRet != SEC_I_CONTINUE_NEEDED) + { + DEBUG_printf(("_sspiConnect: InitializeSecurityContext(1) failed: %x", scRet)); + ok = FALSE; + goto cleanup; + } + + /* + * Send response to server if there is one. + */ + if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) + { + cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0); + + if ((cbData == SOCKET_ERROR) || !cbData) + { + DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError())); + FreeContextBuffer(outBuffers[0].pvBuffer); + DeleteSecurityContext(&conn->context); + ok = FALSE; + goto cleanup; + } + + DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData)); + + /* + * Free output buffer. + */ + FreeContextBuffer(outBuffers[0].pvBuffer); + outBuffers[0].pvBuffer = NULL; + } + + dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION | + ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM; + + conn->decryptBufferUsed = 0; + + /* + * Loop until the handshake is finished or an error occurs. + */ + scRet = SEC_I_CONTINUE_NEEDED; + + while(scRet == SEC_I_CONTINUE_NEEDED || + scRet == SEC_E_INCOMPLETE_MESSAGE || + scRet == SEC_I_INCOMPLETE_CREDENTIALS) + { + if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE)) + { + if (conn->decryptBufferLength <= conn->decryptBufferUsed) + { + conn->decryptBufferLength += 4096; + conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer, conn->decryptBufferLength); + + if (!conn->decryptBuffer) + { + DEBUG_printf(("_sspiConnect: unable to allocate %d byte decrypt buffer", + conn->decryptBufferLength)); + SetLastError(E_OUTOFMEMORY); + ok = FALSE; + goto cleanup; + } + } + + cbData = recv(conn->sock, conn->decryptBuffer + conn->decryptBufferUsed, + (int) (conn->decryptBufferLength - conn->decryptBufferUsed), 0); + + if (cbData == SOCKET_ERROR) + { + DEBUG_printf(("_sspiConnect: recv failed: %d", WSAGetLastError())); + ok = FALSE; + goto cleanup; + } + else if (cbData == 0) + { + DEBUG_printf(("_sspiConnect: server unexpectedly disconnected")); + ok = FALSE; + goto cleanup; + } + + DEBUG_printf(("_sspiConnect: %d bytes of handshake data received", + cbData)); + + conn->decryptBufferUsed += cbData; + } + + /* + * Set up the input buffers. Buffer 0 is used to pass in data + * received from the server. Schannel will consume some or all + * of this. Leftover data (if any) will be placed in buffer 1 and + * given a buffer type of SECBUFFER_EXTRA. + */ + inBuffers[0].pvBuffer = conn->decryptBuffer; + inBuffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed; + inBuffers[0].BufferType = SECBUFFER_TOKEN; + + inBuffers[1].pvBuffer = NULL; + inBuffers[1].cbBuffer = 0; + inBuffers[1].BufferType = SECBUFFER_EMPTY; + + inBuffer.cBuffers = 2; + inBuffer.pBuffers = inBuffers; + inBuffer.ulVersion = SECBUFFER_VERSION; + + /* + * Set up the output buffers. These are initialized to NULL + * so as to make it less likely we'll attempt to free random + * garbage later. + */ + outBuffers[0].pvBuffer = NULL; + outBuffers[0].BufferType= SECBUFFER_TOKEN; + outBuffers[0].cbBuffer = 0; + + outBuffer.cBuffers = 1; + outBuffer.pBuffers = outBuffers; + outBuffer.ulVersion = SECBUFFER_VERSION; + + /* + * Call InitializeSecurityContext. + */ + scRet = InitializeSecurityContext(&conn->creds, &conn->context, NULL, dwSSPIFlags, + 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, + &outBuffer, &dwSSPIOutFlags, &tsExpiry); + + /* + * If InitializeSecurityContext was successful (or if the error was + * one of the special extended ones), send the contends of the output + * buffer to the server. + */ + if (scRet == SEC_E_OK || + scRet == SEC_I_CONTINUE_NEEDED || + FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) + { + if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) + { + cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0); + + if ((cbData == SOCKET_ERROR) || !cbData) + { + DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError())); + FreeContextBuffer(outBuffers[0].pvBuffer); + DeleteSecurityContext(&conn->context); + ok = FALSE; + goto cleanup; + } + + DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData)); + + /* + * Free output buffer. + */ + FreeContextBuffer(outBuffers[0].pvBuffer); + outBuffers[0].pvBuffer = NULL; + } + } + + /* + * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, + * then we need to read more data from the server and try again. + */ + if (scRet == SEC_E_INCOMPLETE_MESSAGE) + continue; + + /* + * If InitializeSecurityContext returned SEC_E_OK, then the + * handshake completed successfully. + */ + if (scRet == SEC_E_OK) + { + /* + * If the "extra" buffer contains data, this is encrypted application + * protocol layer stuff. It needs to be saved. The application layer + * will later decrypt it with DecryptMessage. + */ + DEBUG_printf(("_sspiConnect: Handshake was successful")); + + if (inBuffers[1].BufferType == SECBUFFER_EXTRA) + { + if (conn->decryptBufferLength < inBuffers[1].cbBuffer) + { + conn->decryptBuffer = realloc(conn->decryptBuffer, inBuffers[1].cbBuffer); + + if (!conn->decryptBuffer) + { + DEBUG_printf(("_sspiConnect: unable to allocate %d bytes for decrypt buffer", + inBuffers[1].cbBuffer)); + SetLastError(E_OUTOFMEMORY); + ok = FALSE; + goto cleanup; + } + } + + memmove(conn->decryptBuffer, + conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer), + inBuffers[1].cbBuffer); + + conn->decryptBufferUsed = inBuffers[1].cbBuffer; + + DEBUG_printf(("_sspiConnect: %d bytes of app data was bundled with handshake data", + conn->decryptBufferUsed)); + } + else + conn->decryptBufferUsed = 0; + + /* + * Bail out to quit + */ + break; + } + + /* + * Check for fatal error. + */ + if (FAILED(scRet)) + { + DEBUG_printf(("_sspiConnect: InitializeSecurityContext(2) failed: %x", scRet)); + ok = FALSE; + break; + } + + /* + * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS, + * then the server just requested client authentication. + */ + if (scRet == SEC_I_INCOMPLETE_CREDENTIALS) + { + /* + * Unimplemented + */ + DEBUG_printf(("_sspiConnect: server requested client credentials")); + ok = FALSE; + break; + } + + /* + * Copy any leftover data from the "extra" buffer, and go around + * again. + */ + if (inBuffers[1].BufferType == SECBUFFER_EXTRA) + { + memmove(conn->decryptBuffer, + conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer), + inBuffers[1].cbBuffer); + + conn->decryptBufferUsed = inBuffers[1].cbBuffer; + } + else + { + conn->decryptBufferUsed = 0; + } + } + + if (ok) + { + conn->contextInitialized = TRUE; + + /* + * Get the server cert + */ + scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID*) &serverCert ); + + if (scRet != SEC_E_OK) + { + DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet)); + ok = FALSE; + goto cleanup; + } + + scRet = sspi_verify_certificate(serverCert, hostname, conn->certFlags); + + if (scRet != SEC_E_OK) + { + DEBUG_printf(("_sspiConnect: sspi_verify_certificate failed: %x", scRet)); + ok = FALSE; + goto cleanup; + } + + /* + * Find out how big the header/trailer will be: + */ + scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes); + + if (scRet != SEC_E_OK) + { + DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet)); + ok = FALSE; + } + } + +cleanup: + + if (serverCert) + CertFreeCertificateContext(serverCert); + + return (ok); +} + + +/* + * '_sspiAccept()' - Accept an SSL/TLS connection + */ +BOOL /* O - 1 on success, 0 on failure */ +_sspiAccept(_sspi_struct_t *conn) /* I - Client connection */ +{ + DWORD dwSSPIFlags; /* SSL connection attributes we want */ + DWORD dwSSPIOutFlags; /* SSL connection attributes we got */ + TimeStamp tsExpiry; /* Time stamp */ + SECURITY_STATUS scRet; /* SSPI Status */ + SecBufferDesc inBuffer; /* Array of SecBuffer structs */ + SecBuffer inBuffers[2]; /* Security package buffer */ + SecBufferDesc outBuffer; /* Array of SecBuffer structs */ + SecBuffer outBuffers[1]; /* Security package buffer */ + DWORD num = 0; /* 32 bit status value */ + BOOL fInitContext = TRUE; + /* Has the context been init'd? */ + BOOL ok = TRUE; /* Return value */ + + if (!conn) + return (FALSE); + + dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT | + ASC_REQ_REPLAY_DETECT | + ASC_REQ_CONFIDENTIALITY | + ASC_REQ_EXTENDED_ERROR | + ASC_REQ_ALLOCATE_MEMORY | + ASC_REQ_STREAM; + + conn->decryptBufferUsed = 0; + + /* + * Set OutBuffer for AcceptSecurityContext call + */ + outBuffer.cBuffers = 1; + outBuffer.pBuffers = outBuffers; + outBuffer.ulVersion = SECBUFFER_VERSION; + + scRet = SEC_I_CONTINUE_NEEDED; + + while (scRet == SEC_I_CONTINUE_NEEDED || + scRet == SEC_E_INCOMPLETE_MESSAGE || + scRet == SEC_I_INCOMPLETE_CREDENTIALS) + { + if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE)) + { + if (conn->decryptBufferLength <= conn->decryptBufferUsed) + { + conn->decryptBufferLength += 4096; + conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer, + conn->decryptBufferLength); + + if (!conn->decryptBuffer) + { + DEBUG_printf(("_sspiAccept: unable to allocate %d byte decrypt buffer", + conn->decryptBufferLength)); + ok = FALSE; + goto cleanup; + } + } + + for (;;) + { + num = recv(conn->sock, + conn->decryptBuffer + conn->decryptBufferUsed, + (int)(conn->decryptBufferLength - conn->decryptBufferUsed), + 0); + + if ((num == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK)) + Sleep(1); + else + break; + } + + if (num == SOCKET_ERROR) + { + DEBUG_printf(("_sspiAccept: recv failed: %d", WSAGetLastError())); + ok = FALSE; + goto cleanup; + } + else if (num == 0) + { + DEBUG_printf(("_sspiAccept: client disconnected")); + ok = FALSE; + goto cleanup; + } + + DEBUG_printf(("_sspiAccept: received %d (handshake) bytes from client", + num)); + conn->decryptBufferUsed += num; + } + + /* + * InBuffers[1] is for getting extra data that + * SSPI/SCHANNEL doesn't proccess on this + * run around the loop. + */ + inBuffers[0].pvBuffer = conn->decryptBuffer; + inBuffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed; + inBuffers[0].BufferType = SECBUFFER_TOKEN; + + inBuffers[1].pvBuffer = NULL; + inBuffers[1].cbBuffer = 0; + inBuffers[1].BufferType = SECBUFFER_EMPTY; + + inBuffer.cBuffers = 2; + inBuffer.pBuffers = inBuffers; + inBuffer.ulVersion = SECBUFFER_VERSION; + + /* + * Initialize these so if we fail, pvBuffer contains NULL, + * so we don't try to free random garbage at the quit + */ + outBuffers[0].pvBuffer = NULL; + outBuffers[0].BufferType = SECBUFFER_TOKEN; + outBuffers[0].cbBuffer = 0; + + scRet = AcceptSecurityContext(&conn->creds, (fInitContext?NULL:&conn->context), + &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, + (fInitContext?&conn->context:NULL), &outBuffer, + &dwSSPIOutFlags, &tsExpiry); + + fInitContext = FALSE; + + if (scRet == SEC_E_OK || + scRet == SEC_I_CONTINUE_NEEDED || + (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0))) + { + if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) + { + /* + * Send response to server if there is one + */ + num = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0); + + if ((num == SOCKET_ERROR) || (num == 0)) + { + DEBUG_printf(("_sspiAccept: handshake send failed: %d", WSAGetLastError())); + ok = FALSE; + goto cleanup; + } + + DEBUG_printf(("_sspiAccept: send %d handshake bytes to client", + outBuffers[0].cbBuffer)); + + FreeContextBuffer(outBuffers[0].pvBuffer); + outBuffers[0].pvBuffer = NULL; + } + } + + if (scRet == SEC_E_OK) + { + /* + * If there's extra data then save it for + * next time we go to decrypt + */ + if (inBuffers[1].BufferType == SECBUFFER_EXTRA) + { + memcpy(conn->decryptBuffer, + (LPBYTE) (conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer)), + inBuffers[1].cbBuffer); + conn->decryptBufferUsed = inBuffers[1].cbBuffer; + } + else + { + conn->decryptBufferUsed = 0; + } + + ok = TRUE; + break; + } + else if (FAILED(scRet) && (scRet != SEC_E_INCOMPLETE_MESSAGE)) + { + DEBUG_printf(("_sspiAccept: AcceptSecurityContext failed: %x", scRet)); + ok = FALSE; + break; + } + + if (scRet != SEC_E_INCOMPLETE_MESSAGE && + scRet != SEC_I_INCOMPLETE_CREDENTIALS) + { + if (inBuffers[1].BufferType == SECBUFFER_EXTRA) + { + memcpy(conn->decryptBuffer, + (LPBYTE) (conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer)), + inBuffers[1].cbBuffer); + conn->decryptBufferUsed = inBuffers[1].cbBuffer; + } + else + { + conn->decryptBufferUsed = 0; + } + } + } + + if (ok) + { + conn->contextInitialized = TRUE; + + /* + * Find out how big the header will be: + */ + scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes); + + if (scRet != SEC_E_OK) + { + DEBUG_printf(("_sspiAccept: QueryContextAttributes failed: %x", scRet)); + ok = FALSE; + } + } + +cleanup: + + return (ok); +} + + +/* + * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs + */ +void +_sspiSetAllowsAnyRoot(_sspi_struct_t *conn, + /* I - Client connection */ + BOOL allow) + /* I - Allow any root */ +{ + conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA : + conn->certFlags & ~SECURITY_FLAG_IGNORE_UNKNOWN_CA; +} + + +/* + * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs + */ +void +_sspiSetAllowsExpiredCerts(_sspi_struct_t *conn, + /* I - Client connection */ + BOOL allow) + /* I - Allow expired certs */ +{ + conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID : + conn->certFlags & ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; +} + + +/* + * '_sspiWrite()' - Write a buffer to an ssl socket + */ +int /* O - Bytes written or SOCKET_ERROR */ +_sspiWrite(_sspi_struct_t *conn, /* I - Client connection */ + void *buf, /* I - Buffer */ + size_t len) /* I - Buffer length */ +{ + SecBufferDesc message; /* Array of SecBuffer struct */ + SecBuffer buffers[4] = { 0 }; /* Security package buffer */ + BYTE *buffer = NULL; /* Scratch buffer */ + int bufferLen; /* Buffer length */ + size_t bytesLeft; /* Bytes left to write */ + int index = 0; /* Index into buffer */ + int num = 0; /* Return value */ + + if (!conn || !buf || !len) + { + WSASetLastError(WSAEINVAL); + num = SOCKET_ERROR; + goto cleanup; + } + + bufferLen = conn->streamSizes.cbMaximumMessage + + conn->streamSizes.cbHeader + + conn->streamSizes.cbTrailer; + + buffer = (BYTE*) malloc(bufferLen); + + if (!buffer) + { + DEBUG_printf(("_sspiWrite: buffer alloc of %d bytes failed", bufferLen)); + WSASetLastError(E_OUTOFMEMORY); + num = SOCKET_ERROR; + goto cleanup; + } + + bytesLeft = len; + + while (bytesLeft) + { + size_t chunk = min(conn->streamSizes.cbMaximumMessage, /* Size of data to write */ + bytesLeft); + SECURITY_STATUS scRet; /* SSPI status */ + + /* + * Copy user data into the buffer, starting + * just past the header + */ + memcpy(buffer + conn->streamSizes.cbHeader, + ((BYTE*) buf) + index, + chunk); + + /* + * Setup the SSPI buffers + */ + message.ulVersion = SECBUFFER_VERSION; + message.cBuffers = 4; + message.pBuffers = buffers; + buffers[0].pvBuffer = buffer; + buffers[0].cbBuffer = conn->streamSizes.cbHeader; + buffers[0].BufferType = SECBUFFER_STREAM_HEADER; + buffers[1].pvBuffer = buffer + conn->streamSizes.cbHeader; + buffers[1].cbBuffer = (unsigned long) chunk; + buffers[1].BufferType = SECBUFFER_DATA; + buffers[2].pvBuffer = buffer + conn->streamSizes.cbHeader + chunk; + buffers[2].cbBuffer = conn->streamSizes.cbTrailer; + buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; + buffers[3].BufferType = SECBUFFER_EMPTY; + + /* + * Encrypt the data + */ + scRet = EncryptMessage(&conn->context, 0, &message, 0); + + if (FAILED(scRet)) + { + DEBUG_printf(("_sspiWrite: EncryptMessage failed: %x", scRet)); + WSASetLastError(WSASYSCALLFAILURE); + num = SOCKET_ERROR; + goto cleanup; + } + + /* + * Send the data. Remember the size of + * the total data to send is the size + * of the header, the size of the data + * the caller passed in and the size + * of the trailer + */ + num = send(conn->sock, + buffer, + buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, + 0); + + if ((num == SOCKET_ERROR) || (num == 0)) + { + DEBUG_printf(("_sspiWrite: send failed: %ld", WSAGetLastError())); + goto cleanup; + } + + bytesLeft -= (int) chunk; + index += (int) chunk; + } + + num = (int) len; + +cleanup: + + if (buffer) + free(buffer); + + return (num); +} + + +/* + * '_sspiRead()' - Read a buffer from an ssl socket + */ +int /* O - Bytes read or SOCKET_ERROR */ +_sspiRead(_sspi_struct_t *conn, /* I - Client connection */ + void *buf, /* I - Buffer */ + size_t len) /* I - Buffer length */ +{ + SecBufferDesc message; /* Array of SecBuffer struct */ + SecBuffer buffers[4] = { 0 }; /* Security package buffer */ + int num = 0; /* Return value */ + + if (!conn) + { + WSASetLastError(WSAEINVAL); + num = SOCKET_ERROR; + goto cleanup; + } + + /* + * If there are bytes that have already been + * decrypted and have not yet been read, return + * those + */ + if (buf && (conn->readBufferUsed > 0)) + { + int bytesToCopy = (int) min(conn->readBufferUsed, len); /* Amount of bytes to copy */ + /* from read buffer */ + + memcpy(buf, conn->readBuffer, bytesToCopy); + conn->readBufferUsed -= bytesToCopy; + + if (conn->readBufferUsed > 0) + /* + * If the caller didn't request all the bytes + * we have in the buffer, then move the unread + * bytes down + */ + memmove(conn->readBuffer, + conn->readBuffer + bytesToCopy, + conn->readBufferUsed); + + num = bytesToCopy; + } + else + { + PSecBuffer pDataBuffer; /* Data buffer */ + PSecBuffer pExtraBuffer; /* Excess data buffer */ + SECURITY_STATUS scRet; /* SSPI status */ + int i; /* Loop control variable */ + + /* + * Initialize security buffer structs + */ + message.ulVersion = SECBUFFER_VERSION; + message.cBuffers = 4; + message.pBuffers = buffers; + + do + { + /* + * If there is not enough space in the + * buffer, then increase it's size + */ + if (conn->decryptBufferLength <= conn->decryptBufferUsed) + { + conn->decryptBufferLength += 4096; + conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer, + conn->decryptBufferLength); + + if (!conn->decryptBuffer) + { + DEBUG_printf(("_sspiRead: unable to allocate %d byte buffer", + conn->decryptBufferLength)); + WSASetLastError(E_OUTOFMEMORY); + num = SOCKET_ERROR; + goto cleanup; + } + } + + buffers[0].pvBuffer = conn->decryptBuffer; + buffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed; + buffers[0].BufferType = SECBUFFER_DATA; + buffers[1].BufferType = SECBUFFER_EMPTY; + buffers[2].BufferType = SECBUFFER_EMPTY; + buffers[3].BufferType = SECBUFFER_EMPTY; + + scRet = DecryptMessage(&conn->context, &message, 0, NULL); + + if (scRet == SEC_E_INCOMPLETE_MESSAGE) + { + if (buf) + { + num = recv(conn->sock, + conn->decryptBuffer + conn->decryptBufferUsed, + (int)(conn->decryptBufferLength - conn->decryptBufferUsed), + 0); + if (num == SOCKET_ERROR) + { + DEBUG_printf(("_sspiRead: recv failed: %d", WSAGetLastError())); + goto cleanup; + } + else if (num == 0) + { + DEBUG_printf(("_sspiRead: server disconnected")); + goto cleanup; + } + + conn->decryptBufferUsed += num; + } + else + { + num = (int) conn->readBufferUsed; + goto cleanup; + } + } + } + while (scRet == SEC_E_INCOMPLETE_MESSAGE); + + if (scRet == SEC_I_CONTEXT_EXPIRED) + { + DEBUG_printf(("_sspiRead: context expired")); + WSASetLastError(WSAECONNRESET); + num = SOCKET_ERROR; + goto cleanup; + } + else if (scRet != SEC_E_OK) + { + DEBUG_printf(("_sspiRead: DecryptMessage failed: %lx", scRet)); + WSASetLastError(WSASYSCALLFAILURE); + num = SOCKET_ERROR; + goto cleanup; + } + + /* + * The decryption worked. Now, locate data buffer. + */ + pDataBuffer = NULL; + pExtraBuffer = NULL; + for (i = 1; i < 4; i++) + { + if (buffers[i].BufferType == SECBUFFER_DATA) + pDataBuffer = &buffers[i]; + else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA)) + pExtraBuffer = &buffers[i]; + } + + /* + * If a data buffer is found, then copy + * the decrypted bytes to the passed-in + * buffer + */ + if (pDataBuffer) + { + int bytesToCopy = min(pDataBuffer->cbBuffer, (int) len); + /* Number of bytes to copy into buf */ + int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy; + /* Number of bytes to save in our read buffer */ + + if (bytesToCopy) + memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy); + + /* + * If there are more decrypted bytes than can be + * copied to the passed in buffer, then save them + */ + if (bytesToSave) + { + if ((int)(conn->readBufferLength - conn->readBufferUsed) < bytesToSave) + { + conn->readBufferLength = conn->readBufferUsed + bytesToSave; + conn->readBuffer = realloc(conn->readBuffer, + conn->readBufferLength); + + if (!conn->readBuffer) + { + DEBUG_printf(("_sspiRead: unable to allocate %d bytes", conn->readBufferLength)); + WSASetLastError(E_OUTOFMEMORY); + num = SOCKET_ERROR; + goto cleanup; + } + } + + memcpy(((BYTE*) conn->readBuffer) + conn->readBufferUsed, + ((BYTE*) pDataBuffer->pvBuffer) + bytesToCopy, + bytesToSave); + + conn->readBufferUsed += bytesToSave; + } + + num = (buf) ? bytesToCopy : (int) conn->readBufferUsed; + } + else + { + DEBUG_printf(("_sspiRead: unable to find data buffer")); + WSASetLastError(WSASYSCALLFAILURE); + num = SOCKET_ERROR; + goto cleanup; + } + + /* + * If the decryption process left extra bytes, + * then save those back in decryptBuffer. They will + * be processed the next time through the loop. + */ + if (pExtraBuffer) + { + memmove(conn->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer); + conn->decryptBufferUsed = pExtraBuffer->cbBuffer; + } + else + { + conn->decryptBufferUsed = 0; + } + } + +cleanup: + + return (num); +} + + +/* + * '_sspiPending()' - Returns the number of available bytes + */ +int /* O - Number of available bytes */ +_sspiPending(_sspi_struct_t *conn) /* I - Client connection */ +{ + return (_sspiRead(conn, NULL, 0)); +} + + +/* + * '_sspiFree()' - Close a connection and free resources + */ +void +_sspiFree(_sspi_struct_t *conn) /* I - Client connection */ +{ + if (!conn) + return; + + if (conn->contextInitialized) + { + SecBufferDesc message; /* Array of SecBuffer struct */ + SecBuffer buffers[1] = { 0 }; + /* Security package buffer */ + DWORD dwType; /* Type */ + DWORD status; /* Status */ + + /* + * Notify schannel that we are about to close the connection. + */ + dwType = SCHANNEL_SHUTDOWN; + + buffers[0].pvBuffer = &dwType; + buffers[0].BufferType = SECBUFFER_TOKEN; + buffers[0].cbBuffer = sizeof(dwType); + + message.cBuffers = 1; + message.pBuffers = buffers; + message.ulVersion = SECBUFFER_VERSION; + + status = ApplyControlToken(&conn->context, &message); + + if (SUCCEEDED(status)) + { + PBYTE pbMessage; /* Message buffer */ + DWORD cbMessage; /* Message buffer count */ + DWORD cbData; /* Data count */ + DWORD dwSSPIFlags; /* SSL attributes we requested */ + DWORD dwSSPIOutFlags; /* SSL attributes we received */ + TimeStamp tsExpiry; /* Time stamp */ + + dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT | + ASC_REQ_REPLAY_DETECT | + ASC_REQ_CONFIDENTIALITY | + ASC_REQ_EXTENDED_ERROR | + ASC_REQ_ALLOCATE_MEMORY | + ASC_REQ_STREAM; + + buffers[0].pvBuffer = NULL; + buffers[0].BufferType = SECBUFFER_TOKEN; + buffers[0].cbBuffer = 0; + + message.cBuffers = 1; + message.pBuffers = buffers; + message.ulVersion = SECBUFFER_VERSION; + + status = AcceptSecurityContext(&conn->creds, &conn->context, NULL, + dwSSPIFlags, SECURITY_NATIVE_DREP, NULL, + &message, &dwSSPIOutFlags, &tsExpiry); + + if (SUCCEEDED(status)) + { + pbMessage = buffers[0].pvBuffer; + cbMessage = buffers[0].cbBuffer; + + /* + * Send the close notify message to the client. + */ + if (pbMessage && cbMessage) + { + cbData = send(conn->sock, pbMessage, cbMessage, 0); + if ((cbData == SOCKET_ERROR) || (cbData == 0)) + { + status = WSAGetLastError(); + DEBUG_printf(("_sspiFree: sending close notify failed: %d", status)); + } + else + { + FreeContextBuffer(pbMessage); + } + } + } + else + { + DEBUG_printf(("_sspiFree: AcceptSecurityContext failed: %x", status)); + } + } + else + { + DEBUG_printf(("_sspiFree: ApplyControlToken failed: %x", status)); + } + + DeleteSecurityContext(&conn->context); + conn->contextInitialized = FALSE; + } + + if (conn->decryptBuffer) + { + free(conn->decryptBuffer); + conn->decryptBuffer = NULL; + } + + if (conn->readBuffer) + { + free(conn->readBuffer); + conn->readBuffer = NULL; + } + + if (conn->sock != INVALID_SOCKET) + { + closesocket(conn->sock); + conn->sock = INVALID_SOCKET; + } + + free(conn); +} + + +/* + * 'sspi_verify_certificate()' - Verify a server certificate + */ +static DWORD /* 0 - Error code (0 == No error) */ +sspi_verify_certificate(PCCERT_CONTEXT serverCert, + /* I - Server certificate */ + const CHAR *serverName, + /* I - Server name */ + DWORD dwCertFlags) + /* I - Verification flags */ +{ + HTTPSPolicyCallbackData httpsPolicy; + /* HTTPS Policy Struct */ + CERT_CHAIN_POLICY_PARA policyPara; + /* Cert chain policy parameters */ + CERT_CHAIN_POLICY_STATUS policyStatus; + /* Cert chain policy status */ + CERT_CHAIN_PARA chainPara; + /* Used for searching and matching criteria */ + PCCERT_CHAIN_CONTEXT chainContext = NULL; + /* Certificate chain */ + PWSTR serverNameUnicode = NULL; + /* Unicode server name */ + LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH, + szOID_SERVER_GATED_CRYPTO, + szOID_SGC_NETSCAPE }; + /* How are we using this certificate? */ + DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR); + /* Number of ites in rgszUsages */ + DWORD count; /* 32 bit count variable */ + DWORD status; /* Return value */ + + if (!serverCert) + { + status = SEC_E_WRONG_PRINCIPAL; + goto cleanup; + } + + /* + * Convert server name to unicode. + */ + if (!serverName || (strlen(serverName) == 0)) + { + status = SEC_E_WRONG_PRINCIPAL; + goto cleanup; + } + + count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, NULL, 0); + serverNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR)); + if (!serverNameUnicode) + { + status = SEC_E_INSUFFICIENT_MEMORY; + goto cleanup; + } + count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, serverNameUnicode, count); + if (count == 0) + { + status = SEC_E_WRONG_PRINCIPAL; + goto cleanup; + } + + /* + * Build certificate chain. + */ + ZeroMemory(&chainPara, sizeof(chainPara)); + chainPara.cbSize = sizeof(chainPara); + chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; + chainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages; + chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; + + if (!CertGetCertificateChain(NULL, serverCert, NULL, serverCert->hCertStore, + &chainPara, 0, NULL, &chainContext)) + { + status = GetLastError(); + DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status)); + goto cleanup; + } + + /* + * Validate certificate chain. + */ + ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData)); + httpsPolicy.cbStruct = sizeof(HTTPSPolicyCallbackData); + httpsPolicy.dwAuthType = AUTHTYPE_SERVER; + httpsPolicy.fdwChecks = dwCertFlags; + httpsPolicy.pwszServerName = serverNameUnicode; + + memset(&policyPara, 0, sizeof(policyPara)); + policyPara.cbSize = sizeof(policyPara); + policyPara.pvExtraPolicyPara = &httpsPolicy; + + memset(&policyStatus, 0, sizeof(policyStatus)); + policyStatus.cbSize = sizeof(policyStatus); + + if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, + &policyPara, &policyStatus)) + { + status = GetLastError(); + DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status)); + goto cleanup; + } + + if (policyStatus.dwError) + { + status = policyStatus.dwError; + goto cleanup; + } + + status = SEC_E_OK; + +cleanup: + + if (chainContext) + CertFreeCertificateChain(chainContext); + + if (serverNameUnicode) + LocalFree(serverNameUnicode); + + return (status); +} diff --git a/cups/testi18n.c b/cups/testi18n.c index 76a06cc33..fa5491970 100644 --- a/cups/testi18n.c +++ b/cups/testi18n.c @@ -199,16 +199,6 @@ main(int argc, /* I - Argument Count */ } /* - * Make sure we have a symbolic link from the data directory to a - * "charmaps" directory, and then point the library at it... - */ - - if (access("charmaps", 0)) - symlink("../data", "charmaps"); - - putenv("CUPS_DATADIR=."); - - /* * Start with some conversion tests from a UTF-8 test file. */ @@ -270,62 +260,6 @@ main(int argc, /* I - Argument Count */ fclose(fp); /* - * Test charmap load for ISO-8859-1... - */ - - fputs("_cupsCharmapGet(CUPS_ISO8859_1): ", stdout); - - if (!_cupsCharmapGet(CUPS_ISO8859_1)) - { - errors ++; - puts("FAIL"); - } - else - puts("PASS"); - - /* - * Test charmap load for Windows-932 (Shift-JIS)... - */ - - fputs("_cupsCharmapGet(CUPS_WINDOWS_932): ", stdout); - - if (!_cupsCharmapGet(CUPS_WINDOWS_932)) - { - errors ++; - puts("FAIL"); - } - else - puts("PASS"); - - /* - * Test VBCS charmap load for EUC-JP... - */ - - fputs("_cupsCharmapGet(CUPS_EUC_JP): ", stdout); - - if (!_cupsCharmapGet(CUPS_EUC_JP)) - { - errors ++; - puts("FAIL"); - } - else - puts("PASS"); - - /* - * Test VBCS charmap load for EUC-TW... - */ - - fputs("_cupsCharmapGet(CUPS_EUC_TW): ", stdout); - - if (!_cupsCharmapGet(CUPS_EUC_TW)) - { - errors ++; - puts("FAIL"); - } - else - puts("PASS"); - - /* * Test UTF-8 to legacy charset (ISO 8859-1)... */ diff --git a/cups/testppd.c b/cups/testppd.c index db3037b67..9fe4d562b 100644 --- a/cups/testppd.c +++ b/cups/testppd.c @@ -441,6 +441,20 @@ main(int argc, /* I - Number of command-line arguments */ else puts("PASS"); + fputs("cupsMarkOptions(media=A4): ", stdout); + num_options = cupsAddOption("media", "A4", 0, &options); + cupsMarkOptions(ppd, num_options, options); + cupsFreeOptions(num_options, options); + + size = ppdPageSize(ppd, NULL); + if (!size || strcmp(size->name, "A4")) + { + printf("FAIL (%s)\n", size ? size->name : "unknown"); + status ++; + } + else + puts("PASS"); + /* * Test localization... */ diff --git a/cups/transcode.c b/cups/transcode.c index 3a00fcd9a..7b98f2518 100644 --- a/cups/transcode.c +++ b/cups/transcode.c @@ -17,23 +17,10 @@ * Contents: * * _cupsCharmapFlush() - Flush all character set maps out of cache. - * _cupsCharmapFree() - Free a character set map. - * _cupsCharmapGet() - Get a character set map. * cupsCharsetToUTF8() - Convert legacy character set to UTF-8. * cupsUTF8ToCharset() - Convert UTF-8 to legacy character set. * cupsUTF8ToUTF32() - Convert UTF-8 to UTF-32. * cupsUTF32ToUTF8() - Convert UTF-32 to UTF-8. - * compare_wide() - Compare key for wide (VBCS) match. - * conv_sbcs_to_utf8() - Convert legacy SBCS to UTF-8. - * conv_utf8_to_sbcs() - Convert UTF-8 to legacy SBCS. - * conv_utf8_to_vbcs() - Convert UTF-8 to legacy DBCS/VBCS. - * conv_vbcs_to_utf8() - Convert legacy DBCS/VBCS to UTF-8. - * free_sbcs_charmap() - Free memory used by a single byte character set. - * free_vbcs_charmap() - Free memory used by a variable byte character set. - * get_charmap() - Lookup or get a character set map (private). - * get_charmap_count() - Count lines in a charmap file. - * get_sbcs_charmap() - Get SBCS Charmap. - * get_vbcs_charmap() - Get DBCS/VBCS Charmap. */ /* @@ -43,49 +30,25 @@ #include "cups-private.h" #include <limits.h> #include <time.h> +#ifdef HAVE_ICONV_H +# include <iconv.h> +#endif /* HAVE_ICONV_H */ /* * Local globals... */ +#ifdef HAVE_ICONV_H static _cups_mutex_t map_mutex = _CUPS_MUTEX_INITIALIZER; /* Mutex to control access to maps */ -static _cups_cmap_t *cmap_cache = NULL; - /* SBCS Charmap Cache */ -static _cups_vmap_t *vmap_cache = NULL; - /* VBCS Charmap Cache */ - - -/* - * Local functions... - */ - -static int compare_wide(const void *k1, const void *k2); -static int conv_sbcs_to_utf8(cups_utf8_t *dest, - const cups_sbcs_t *src, - int maxout, - const cups_encoding_t encoding); -static int conv_utf8_to_sbcs(cups_sbcs_t *dest, - const cups_utf8_t *src, - int maxout, - const cups_encoding_t encoding); -static int conv_utf8_to_vbcs(cups_sbcs_t *dest, - const cups_utf8_t *src, - int maxout, - const cups_encoding_t encoding); -static int conv_vbcs_to_utf8(cups_utf8_t *dest, - const cups_sbcs_t *src, - int maxout, - const cups_encoding_t encoding); -static void free_sbcs_charmap(_cups_cmap_t *sbcs); -static void free_vbcs_charmap(_cups_vmap_t *vbcs); -static void *get_charmap(const cups_encoding_t encoding); -static int get_charmap_count(cups_file_t *fp); -static _cups_cmap_t *get_sbcs_charmap(const cups_encoding_t encoding, - const char *filename); -static _cups_vmap_t *get_vbcs_charmap(const cups_encoding_t encoding, - const char *filename); +static iconv_t map_from_utf8 = (iconv_t)-1; + /* Convert from UTF-8 to charset */ +static iconv_t map_to_utf8 = (iconv_t)-1; + /* Convert from charset to UTF-8 */ +static cups_encoding_t map_encoding = CUPS_AUTO_ENCODING; + /* Which charset is cached */ +#endif /* HAVE_ICONV_H */ /* @@ -95,151 +58,39 @@ static _cups_vmap_t *get_vbcs_charmap(const cups_encoding_t encoding, void _cupsCharmapFlush(void) { - _cups_cmap_t *cmap, /* Legacy SBCS / Unicode Charset Map */ - *cnext; /* Next Legacy SBCS Charset Map */ - _cups_vmap_t *vmap, /* Legacy VBCS / Unicode Charset Map */ - *vnext; /* Next Legacy VBCS Charset Map */ - - - _cupsMutexLock(&map_mutex); - - /* - * Loop through SBCS charset map cache, free all memory... - */ - - for (cmap = cmap_cache; cmap; cmap = cnext) +#ifdef HAVE_ICONV_H + if (map_from_utf8 != (iconv_t)-1) { - cnext = cmap->next; - - free_sbcs_charmap(cmap); + iconv_close(map_from_utf8); + map_from_utf8 = (iconv_t)-1; } - cmap_cache = NULL; - - /* - * Loop through DBCS/VBCS charset map cache, free all memory... - */ - - for (vmap = vmap_cache; vmap; vmap = vnext) - { - vnext = vmap->next; - - free_vbcs_charmap(vmap); - } - - vmap_cache = NULL; - - _cupsMutexUnlock(&map_mutex); -} - - -/* - * '_cupsCharmapFree()' - Free a character set map. - * - * This does not actually free; use '_cupsCharmapFlush()' for that. - */ - -void -_cupsCharmapFree( - const cups_encoding_t encoding) /* I - Encoding */ -{ - _cups_cmap_t *cmap; /* Legacy SBCS / Unicode Charset Map */ - _cups_vmap_t *vmap; /* Legacy VBCS / Unicode Charset Map */ - - - /* - * See if we already have this SBCS charset map loaded... - */ - - _cupsMutexLock(&map_mutex); - - for (cmap = cmap_cache; cmap; cmap = cmap->next) - { - if (cmap->encoding == encoding) - { - if (cmap->used > 0) - cmap->used --; - break; - } - } - - /* - * See if we already have this DBCS/VBCS charset map loaded... - */ - - for (vmap = vmap_cache; vmap; vmap = vmap->next) - { - if (vmap->encoding == encoding) - { - if (vmap->used > 0) - vmap->used --; - break; - } - } - - _cupsMutexUnlock(&map_mutex); -} - - -/* - * '_cupsCharmapGet()' - Get a character set map. - * - * This code handles single-byte (SBCS), double-byte (DBCS), and - * variable-byte (VBCS) character sets _without_ charset escapes... - * This code does not handle multiple-byte character sets (MBCS) - * (such as ISO-2022-JP) with charset switching via escapes... - */ - -void * /* O - Charset map pointer */ -_cupsCharmapGet( - const cups_encoding_t encoding) /* I - Encoding */ -{ - void *charmap; /* Charset map pointer */ - - - DEBUG_printf(("7_cupsCharmapGet(encoding=%d)", encoding)); - - /* - * Check for valid arguments... - */ - - if (encoding < 0 || encoding >= CUPS_ENCODING_VBCS_END) + if (map_to_utf8 != (iconv_t)-1) { - DEBUG_puts("8_cupsCharmapGet: Bad encoding, returning NULL!"); - return (NULL); + iconv_close(map_to_utf8); + map_to_utf8 = (iconv_t)-1; } - /* - * Lookup or get the charset map pointer and return... - */ - - _cupsMutexLock(&map_mutex); - - charmap = get_charmap(encoding); - - _cupsMutexUnlock(&map_mutex); - - return (charmap); + map_encoding = CUPS_AUTO_ENCODING; +#endif /* HAVE_ICONV_H */ } /* * 'cupsCharsetToUTF8()' - Convert legacy character set to UTF-8. - * - * This code handles single-byte (SBCS), double-byte (DBCS), and - * variable-byte (VBCS) character sets _without_ charset escapes... - * This code does not handle multiple-byte character sets (MBCS) - * (such as ISO-2022-JP) with charset switching via escapes... */ int /* O - Count or -1 on error */ cupsCharsetToUTF8( - cups_utf8_t *dest, /* O - Target string */ - const char *src, /* I - Source string */ - const int maxout, /* I - Max output */ + cups_utf8_t *dest, /* O - Target string */ + const char *src, /* I - Source string */ + const int maxout, /* I - Max output */ const cups_encoding_t encoding) /* I - Encoding */ { - int bytes; /* Number of bytes converted */ + cups_utf8_t *destptr; /* Pointer into UTF-8 buffer */ + int bytes; /* Number of bytes converted */ + size_t srclen, /* Length of source string */ + outBytesLeft; /* Bytes remaining in output buffer */ /* @@ -249,11 +100,11 @@ cupsCharsetToUTF8( DEBUG_printf(("2cupsCharsetToUTF8(dest=%p, src=\"%s\", maxout=%d, encoding=%d)", dest, src, maxout, encoding)); - if (dest) - *dest = '\0'; - - if (!dest || !src || maxout < 1 || maxout > CUPS_MAX_USTRING) + if (!dest || !src || maxout < 1) { + if (dest) + *dest = '\0'; + DEBUG_puts("3cupsCharsetToUTF8: Bad arguments, returning -1"); return (-1); } @@ -262,8 +113,8 @@ cupsCharsetToUTF8( * Handle identity conversions... */ - if (encoding == CUPS_UTF8 || - encoding < 0 || encoding >= CUPS_ENCODING_VBCS_END) + if (encoding == CUPS_UTF8 || encoding <= CUPS_US_ASCII || + encoding >= CUPS_ENCODING_VBCS_END) { strlcpy((char *)dest, src, maxout); return ((int)strlen((char *)dest)); @@ -273,14 +124,14 @@ cupsCharsetToUTF8( * Handle ISO-8859-1 to UTF-8 directly... */ + destptr = dest; + if (encoding == CUPS_ISO8859_1) { int ch; /* Character from string */ - cups_utf8_t *destptr, /* Pointer into UTF-8 buffer */ - *destend; /* End of UTF-8 buffer */ + cups_utf8_t *destend; /* End of UTF-8 buffer */ - destptr = dest; destend = dest + maxout - 2; while (*src && destptr < destend) @@ -305,26 +156,46 @@ cupsCharsetToUTF8( * Convert input legacy charset to UTF-8... */ +#ifdef HAVE_ICONV_H _cupsMutexLock(&map_mutex); - if (encoding < CUPS_ENCODING_SBCS_END) - bytes = conv_sbcs_to_utf8(dest, (cups_sbcs_t *)src, maxout, encoding); - else - bytes = conv_vbcs_to_utf8(dest, (cups_sbcs_t *)src, maxout, encoding); + if (map_encoding != encoding) + { + _cupsCharmapFlush(); + + map_from_utf8 = iconv_open(_cupsEncodingName(encoding), "UTF-8"); + map_to_utf8 = iconv_open("UTF-8", _cupsEncodingName(encoding)); + map_encoding = encoding; + } + + if (map_to_utf8 != (iconv_t)-1) + { + srclen = strlen(src); + outBytesLeft = maxout - 1; + bytes = (int)iconv(map_to_utf8, (char **)&src, &srclen, + (char **)&destptr, &outBytesLeft); + *destptr = '\0'; + + _cupsMutexUnlock(&map_mutex); + + return ((int)(destptr - dest)); + } _cupsMutexUnlock(&map_mutex); +#endif /* HAVE_ICONV_H */ - return (bytes); + /* + * No iconv() support, so error out... + */ + + *destptr = '\0'; + + return (-1); } /* * 'cupsUTF8ToCharset()' - Convert UTF-8 to legacy character set. - * - * This code handles single-byte (SBCS), double-byte (DBCS), and - * variable-byte (VBCS) character sets _without_ charset escapes... - * This code does not handle multiple-byte character sets (MBCS) - * (such as ISO-2022-JP) with charset switching via escapes... */ int /* O - Count or -1 on error */ @@ -334,14 +205,17 @@ cupsUTF8ToCharset( const int maxout, /* I - Max output */ const cups_encoding_t encoding) /* I - Encoding */ { - int bytes; /* Number of bytes converted */ + char *destptr; /* Pointer into destination */ + int bytes; /* Number of bytes converted */ + size_t srclen, /* Length of source string */ + outBytesLeft; /* Bytes remaining in output buffer */ /* * Check for valid arguments... */ - if (!dest || !src || maxout < 1 || maxout > CUPS_MAX_USTRING) + if (!dest || !src || maxout < 1) { if (dest) *dest = '\0'; @@ -353,8 +227,8 @@ cupsUTF8ToCharset( * Handle identity conversions... */ - if (encoding == CUPS_UTF8 || - encoding < 0 || encoding >= CUPS_ENCODING_VBCS_END) + if (encoding == CUPS_UTF8 || encoding <= CUPS_US_ASCII || + encoding >= CUPS_ENCODING_VBCS_END) { strlcpy(dest, (char *)src, maxout); return ((int)strlen(dest)); @@ -364,14 +238,14 @@ cupsUTF8ToCharset( * Handle UTF-8 to ISO-8859-1 directly... */ + destptr = dest; + if (encoding == CUPS_ISO8859_1) { int ch; /* Character from string */ - char *destptr, /* Pointer into ISO-8859-1 buffer */ - *destend; /* End of ISO-8859-1 buffer */ + char *destend; /* End of ISO-8859-1 buffer */ - destptr = dest; destend = dest + maxout - 1; while (*src && destptr < destend) @@ -399,20 +273,45 @@ cupsUTF8ToCharset( return ((int)(destptr - dest)); } +#ifdef HAVE_ICONV_H /* * Convert input UTF-8 to legacy charset... */ _cupsMutexLock(&map_mutex); - if (encoding < CUPS_ENCODING_SBCS_END) - bytes = conv_utf8_to_sbcs((cups_sbcs_t *)dest, src, maxout, encoding); - else - bytes = conv_utf8_to_vbcs((cups_sbcs_t *)dest, src, maxout, encoding); + if (map_encoding != encoding) + { + _cupsCharmapFlush(); + + map_from_utf8 = iconv_open(_cupsEncodingName(encoding), "UTF-8"); + map_to_utf8 = iconv_open("UTF-8", _cupsEncodingName(encoding)); + map_encoding = encoding; + } + + if (map_from_utf8 != (iconv_t)-1) + { + srclen = strlen((char *)src); + outBytesLeft = maxout - 1; + bytes = (int)iconv(map_from_utf8, (char **)&src, &srclen, + &destptr, &outBytesLeft); + *destptr = '\0'; + + _cupsMutexUnlock(&map_mutex); + + return ((int)(destptr - dest)); + } _cupsMutexUnlock(&map_mutex); +#endif /* HAVE_ICONV_H */ + + /* + * No iconv() support, so error out... + */ + + *destptr = '\0'; - return (bytes); + return (-1); } |