From: mike Date: Mon, 23 Oct 2006 00:11:55 +0000 (+0000) Subject: Kerberos fixes for Mac OS X (STR #2045). X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fcups.git;a=commitdiff_plain;h=a99d043e59bec81e646fb389844c4b96941effbb Kerberos fixes for Mac OS X (STR #2045). config-scripts/cups-gssapi.m4: config.h.in: scheduler/conf.c: scheduler/conf.h: - Support configurable kerberos service name, default is "IPP". cups/http.c: - httpConnectEncrypt(): Initialize the gss context and name. - httpClose(): Free the gss context and name. - http_send(): Clear the kerberos authentication string since it can only be sent once. - http_upgrade(): Clear the copy of the field_authorization pointer to avoid a double free later. cups/http-support.c: - httpEncode64_2(): Could reference a byte beyond the end of the input string. cups/auth.c: - cupsDoAuthentication(): Support a configurable kerberos service name, clear gsssec context to avoid kerberos "request is a replay" errors, free allocated input_token and output_token, fix token length, and don't memset 'token' since it's only a pointer. scheduler/auth.c: - cupsdAuthorize(): Support configurable kerberos service name and free gss context and name in the right places. - cupsdIsAuthorized(): Don't require TLS upgrade when using Kerberos, and fix token length. scheduler/ipp.c: - save_krb5_creds(): Doesn't yet work on Mac OS X. scheduler/main.c: - main(): Limit MaxFDs to FD_SETSIZE (from 1.2 branch). git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@6055 7a7537e8-13f0-0310-91df-b6672ffda945 --- diff --git a/cgi-bin/Makefile b/cgi-bin/Makefile index 7fb8a24eb..d7c5709c4 100644 --- a/cgi-bin/Makefile +++ b/cgi-bin/Makefile @@ -139,7 +139,7 @@ printers.cgi: printers.o ../Makedefs ../cups/$(LIBCUPS) libcgi.a testcgi: testcgi.o ../Makedefs libcgi.a ../cups/libcups.a echo Linking $@... $(CC) $(LDFLAGS) -o $@ testcgi.o libcgi.a ../cups/libcups.a \ - $(COMMONLIBS) $(SSLLIBS) $(LIBZ) + $(COMMONLIBS) $(SSLLIBS) $(LIBZ) $(LIBGSSAPI) # @@ -149,7 +149,7 @@ testcgi: testcgi.o ../Makedefs libcgi.a ../cups/libcups.a testhi: testhi.o ../Makedefs libcgi.a ../cups/libcups.a echo Linking $@... $(CC) $(LDFLAGS) -o $@ testhi.o libcgi.a ../cups/libcups.a \ - $(COMMONLIBS) $(SSLLIBS) $(LIBZ) + $(COMMONLIBS) $(SSLLIBS) $(LIBZ) $(LIBGSSAPI) # @@ -159,7 +159,7 @@ testhi: testhi.o ../Makedefs libcgi.a ../cups/libcups.a testtemplate: testtemplate.o ../Makedefs libcgi.a ../cups/libcups.a echo Linking $@... $(CC) $(LDFLAGS) -o $@ testtemplate.o libcgi.a ../cups/libcups.a \ - $(COMMONLIBS) $(SSLLIBS) $(LIBZ) + $(COMMONLIBS) $(SSLLIBS) $(LIBZ) $(LIBGSSAPI) # diff --git a/config-scripts/cups-gssapi.m4 b/config-scripts/cups-gssapi.m4 index 8a78ddbd4..07c527836 100644 --- a/config-scripts/cups-gssapi.m4 +++ b/config-scripts/cups-gssapi.m4 @@ -82,7 +82,23 @@ if test x$enable_gssapi != xno; then fi fi +dnl Default GSS service name... +AC_ARG_WITH(gssservicename, [ --with-gssservicename set default gss service name], + default_gssservicename="$withval", + default_gssservicename="default") + +if test x$default_gssservicename != xno; then + if test "x$default_gssservicename" = "xdefault"; then + CUPS_DEFAULT_GSSSERVICENAME="IPP" + else + CUPS_DEFAULT_GSSSERVICENAME="$default_gssservicename" + fi +else + CUPS_DEFAULT_GSSSERVICENAME="" +fi + AC_SUBST(LIBGSSAPI) +AC_DEFINE_UNQUOTED(CUPS_DEFAULT_GSSSERVICENAME, "$CUPS_DEFAULT_GSSSERVICENAME") dnl dnl End of "$Id$". diff --git a/config.h.in b/config.h.in index b5bb16e82..8fd160f54 100644 --- a/config.h.in +++ b/config.h.in @@ -466,6 +466,13 @@ #undef HAVE_HEIMDAL +/* + * Default GSS service name... + */ + +#define CUPS_DEFAULT_GSSSERVICENAME "" + + #endif /* !_CUPS_CONFIG_H_ */ /* diff --git a/cups/auth.c b/cups/auth.c index 622318851..3ec00446b 100644 --- a/cups/auth.c +++ b/cups/auth.c @@ -168,9 +168,49 @@ cupsDoAuthentication(http_t *http, /* I - HTTP connection to server */ /* Output token */ input_token = GSS_C_EMPTY_BUFFER; /* Input token */ + char *gss_service_name; + /* GSS service name */ + const char *authorization; + /* Pointer into Authorization string */ - http->gssname = cups_get_gss_creds(http, "HTTP"); + if (http->gssname == GSS_C_NO_NAME) + { + if ((gss_service_name = getenv("CUPS_GSSSERVICENAME")) == NULL) + gss_service_name = CUPS_DEFAULT_GSSSERVICENAME; + else + DEBUG_puts("cupsDoAuthentication: GSS service name set via environment"); + + http->gssname = cups_get_gss_creds(http, gss_service_name); + } + + /* + * Find the start of the Kerberos input token... + */ + + authorization = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE); + + authorization += 9; + while (*authorization && isspace(*authorization & 255)) + authorization ++; + + if (*authorization) + { + /* + * For SPNEGO, this is where we'll feed the server's authorization data + * back into gss via input_token... + */ + } + else + { + if (http->gssctx != GSS_C_NO_CONTEXT) + { + major_status = gss_delete_sec_context(&minor_status, &http->gssctx, + GSS_C_NO_BUFFER); + http->gssctx = GSS_C_NO_CONTEXT; + } + } + major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, &http->gssctx, http->gssname, http->gssmech, @@ -179,6 +219,9 @@ cupsDoAuthentication(http_t *http, /* I - HTTP connection to server */ &input_token, &http->gssmech, &output_token, NULL, NULL); + if (input_token.value) + free(input_token.value); + if (GSS_ERROR(major_status)) { # ifdef DEBUG @@ -193,11 +236,16 @@ cupsDoAuthentication(http_t *http, /* I - HTTP connection to server */ DEBUG_gss_printf(major_status, minor_status, "Continuation needed!"); # endif /* DEBUG */ - httpEncode64_2(encode, sizeof(encode), output_token.value, - output_token.length); + if (output_token.length) + { + httpEncode64_2(encode, sizeof(encode), output_token.value, + output_token.length); - http->authstring = malloc(strlen(encode) + 20); - sprintf(http->authstring, "Negotiate %s", encode); /* Safe because allocated */ + http->authstring = malloc(strlen(encode) + 11); + sprintf(http->authstring, "Negotiate %s", encode); /* Safe because allocated */ + + major_status = gss_release_buffer(&minor_status, &output_token); + } /* * Copy back what we can to _authstring for backwards compatibility... @@ -310,18 +358,12 @@ cups_get_gss_creds( httpGetHostname(http, fqdn, sizeof(fqdn))); token.value = buf; - token.length = strlen(buf) + 1; + token.length = strlen(buf); server_name = GSS_C_NO_NAME; major_status = gss_import_name(&minor_status, &token, GSS_C_NT_HOSTBASED_SERVICE, &server_name); - /* - * Clear the service token after we are done to avoid exposing information... - */ - - memset(&token, 0, sizeof(token)); - if (GSS_ERROR(major_status)) { # ifdef DEBUG diff --git a/cups/http-support.c b/cups/http-support.c index 456800e79..0f9ef2e9e 100644 --- a/cups/http-support.c +++ b/cups/http-support.c @@ -595,8 +595,14 @@ httpEncode64_2(char *out, /* I - String to write to */ if (outptr < outend) *outptr ++ = base64[(in[0] & 255) >> 2]; + if (outptr < outend) - *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63]; + { + if (inlen > 1) + *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63]; + else + *outptr ++ = base64[((in[0] & 255) << 4) & 63]; + } in ++; inlen --; @@ -610,7 +616,12 @@ httpEncode64_2(char *out, /* I - String to write to */ } if (outptr < outend) - *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63]; + { + if (inlen > 1) + *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63]; + else + *outptr ++ = base64[((in[0] & 255) << 2) & 63]; + } in ++; inlen --; diff --git a/cups/http.c b/cups/http.c index f32eb3699..222a4ba66 100644 --- a/cups/http.c +++ b/cups/http.c @@ -248,6 +248,12 @@ httpClearFields(http_t *http) /* I - HTTP connection */ void httpClose(http_t *http) /* I - HTTP connection */ { +#ifdef HAVE_GSSAPI + OM_uint32 minor_status, /* Minor status code */ + major_status; /* Major status code */ +#endif /* HAVE_GSSAPI */ + + DEBUG_printf(("httpClose(http=%p)\n", http)); if (!http) @@ -272,6 +278,15 @@ httpClose(http_t *http) /* I - HTTP connection */ close(http->fd); #endif /* WIN32 */ +#ifdef HAVE_GSSAPI + if (http->gssctx != GSS_C_NO_CONTEXT) + major_status = gss_delete_sec_context(&minor_status, &http->gssctx, + GSS_C_NO_BUFFER); + + if (http->gssname != GSS_C_NO_NAME) + major_status = gss_release_name(&minor_status, &http->gssname); +#endif /* HAVE_GSSAPI */ + httpClearFields(http); if (http->authstring && http->authstring != http->_authstring) @@ -350,6 +365,11 @@ httpConnectEncrypt( http->activity = time(NULL); http->fd = -1; +#ifdef HAVE_GSSAPI + http->gssctx = GSS_C_NO_CONTEXT; + http->gssname = GSS_C_NO_NAME; +#endif /* HAVE_GSSAPI */ + /* * Set the encryption status... */ @@ -2270,6 +2290,20 @@ http_send(http_t *http, /* I - HTTP connection */ httpGetLength2(http); httpClearFields(http); + /* + * The Kerberos authentication string can only be used once... + */ + + if (http->authstring && !strncmp(http->authstring, "Negotiate", 9)) + { + http->_authstring[0] = '\0'; + + if (http->authstring != http->_authstring) + free(http->authstring); + + http->authstring = http->_authstring; + } + return (0); } @@ -2504,6 +2538,7 @@ http_upgrade(http_t *http) /* I - HTTP connection */ */ memcpy(&myhttp, http, sizeof(myhttp)); + myhttp.field_authorization = NULL; /* * Send an OPTIONS request to the server, requiring SSL or TLS diff --git a/scheduler/Makefile b/scheduler/Makefile index 20b281cc3..63e921dbf 100644 --- a/scheduler/Makefile +++ b/scheduler/Makefile @@ -261,7 +261,7 @@ testdirsvc: testdirsvc.o testlpd: testlpd.o ../cups/libcups.a cups-lpd echo Linking $@... $(CC) $(LDFLAGS) -o testlpd testlpd.o ../cups/libcups.a \ - $(COMMONLIBS) $(LIBZ) $(SSLLIBS) + $(COMMONLIBS) $(LIBZ) $(SSLLIBS) $(LIBGSSAPI) # @@ -271,7 +271,7 @@ testlpd: testlpd.o ../cups/libcups.a cups-lpd testmime: testmime.o libmime.a ../cups/libcups.a echo Linking $@... $(CC) $(LDFLAGS) -o $@ testmime.o libmime.a ../cups/libcups.a \ - $(COMMONLIBS) $(LIBZ) $(SSLLIBS) + $(COMMONLIBS) $(LIBZ) $(SSLLIBS) $(LIBGSSAPI) # @@ -281,7 +281,7 @@ testmime: testmime.o libmime.a ../cups/libcups.a testspeed: testspeed.o ../cups/libcups.a echo Linking $@... $(CC) $(LDFLAGS) -o testspeed testspeed.o ../cups/libcups.a \ - $(LIBGSSAPI) $(SSLLIBS) $(COMMONLIBS) $(LIBZ) + $(SSLLIBS) $(COMMONLIBS) $(LIBZ) $(LIBGSSAPI) # @@ -291,7 +291,7 @@ testspeed: testspeed.o ../cups/libcups.a testsub: testsub.o ../cups/libcups.a echo Linking $@... $(CC) $(LDFLAGS) -o testsub testsub.o ../cups/libcups.a \ - $(LIBGSSAPI) $(SSLLIBS) $(COMMONLIBS) $(LIBZ) + $(SSLLIBS) $(COMMONLIBS) $(LIBZ) $(LIBGSSAPI) # diff --git a/scheduler/auth.c b/scheduler/auth.c index 80596eacf..6caaae581 100644 --- a/scheduler/auth.c +++ b/scheduler/auth.c @@ -783,7 +783,7 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */ */ authorization += 9; - while (*authorization && isspace(*authorization & 255)) + while (isspace(*authorization & 255)) authorization ++; if (!*authorization) @@ -797,7 +797,7 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */ * Get the server credentials... */ - if ((server_creds = get_gss_creds("HTTP")) == NULL) + if ((server_creds = get_gss_creds(GSSServiceName)) == NULL) { con->no_negotiate = 1; return; @@ -836,6 +836,10 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */ cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status, "cupsdAuthorize: Error accepting GSSAPI security " "context"); + + if (context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER); + con->no_negotiate = 1; return; } @@ -848,19 +852,22 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */ { major_status = gss_display_name(&minor_status, client_name, &output_token, NULL); - gss_release_name(&minor_status, &client_name); if (GSS_ERROR(major_status)) { cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status, "cupsdAuthorize: Error getting username"); + gss_release_name(&minor_status, &client_name); + gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER); con->no_negotiate = 1; return; } + gss_release_name(&minor_status, &client_name); strlcpy(username, output_token.value, sizeof(username)); gss_release_buffer(&minor_status, &output_token); + gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER); } else gss_release_name(&minor_status, &client_name); @@ -1736,9 +1743,11 @@ cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */ * See if encryption is required... */ - if (best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http.tls && + if ((best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http.tls && strcasecmp(con->http.hostname, "localhost") && - best->satisfy == AUTH_SATISFY_ALL) + best->satisfy == AUTH_SATISFY_ALL) && + !(best->type == AUTH_KERBEROS || + (best->type == AUTH_NONE && DefaultAuthType == AUTH_KERBEROS))) { cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Need upgrade to TLS..."); @@ -2128,7 +2137,7 @@ get_gss_creds(const char *service_name) /* I - Service name */ httpGetHostname(NULL, fqdn, sizeof(fqdn))); token.value = buf; - token.length = strlen(buf) + 1; + token.length = strlen(buf); server_name = GSS_C_NO_NAME; major_status = gss_import_name(&minor_status, &token, GSS_C_NT_HOSTBASED_SERVICE, diff --git a/scheduler/conf.c b/scheduler/conf.c index 6aa0b916c..27399975d 100644 --- a/scheduler/conf.c +++ b/scheduler/conf.c @@ -117,7 +117,9 @@ static cupsd_var_t variables[] = { "FilterLimit", &FilterLimit, CUPSD_VARTYPE_INTEGER }, { "FilterNice", &FilterNice, CUPSD_VARTYPE_INTEGER }, { "FontPath", &FontPath, CUPSD_VARTYPE_STRING }, - { "HideImplicitMembers", &HideImplicitMembers, CUPSD_VARTYPE_BOOLEAN }, +#ifdef HAVE_GSSAPI + { "GSSServiceName", &GSSServiceName, CUPSD_VARTYPE_STRING }, +#endif /* HAVE_GSSAPI */ { "ImplicitClasses", &ImplicitClasses, CUPSD_VARTYPE_BOOLEAN }, { "ImplicitAnyClasses", &ImplicitAnyClasses, CUPSD_VARTYPE_BOOLEAN }, { "JobRetryLimit", &JobRetryLimit, CUPSD_VARTYPE_INTEGER }, @@ -288,6 +290,9 @@ cupsdReadConfiguration(void) cupsdSetString(&RemoteRoot, "remroot"); cupsdSetString(&ServerHeader, "CUPS/1.2"); cupsdSetString(&StateDir, CUPS_STATEDIR); +#ifdef HAVE_GSSAPI + cupsdSetString(&GSSServiceName, CUPS_DEFAULT_GSSSERVICENAME); +#endif /* HAVE_GSSAPI */ if (!strcmp(CUPS_DEFAULT_PRINTCAP, "/etc/printers.conf")) PrintcapFormat = PRINTCAP_SOLARIS; @@ -1706,7 +1711,8 @@ parse_aaa(cupsd_location_t *loc, /* I - Location */ loc->level = AUTH_USER; } #ifdef HAVE_GSSAPI - else if (!strcasecmp(value, "kerberos")) + else if (!strcasecmp(value, "kerberos") || + !strcasecmp(value, "gssapi")) { loc->type = AUTH_KERBEROS; diff --git a/scheduler/conf.h b/scheduler/conf.h index 78e4da8c2..d6b2f9250 100644 --- a/scheduler/conf.h +++ b/scheduler/conf.h @@ -112,6 +112,8 @@ VAR char *AccessLog VALUE(NULL), *Classification VALUE(NULL); /* Classification of system */ #ifdef HAVE_GSSAPI +VAR char *GSSServiceName VALUE(NULL); + /* GSS service name */ VAR char *Krb5Keytab VALUE(NULL); /* Kerberos Keytab */ #endif /* HAVE_GSSAPI */ diff --git a/scheduler/ipp.c b/scheduler/ipp.c index 277975814..a711cbfb1 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -7576,6 +7576,7 @@ static void save_krb5_creds(cupsd_client_t *con, /* I - Client connection */ cupsd_job_t *job) /* I - Job */ { +# ifndef __APPLE__ krb5_context krb_context; /* Kerberos context */ krb5_ccache ccache; /* Credentials cache */ OM_uint32 major_status, /* Major status code */ @@ -7616,6 +7617,7 @@ save_krb5_creds(cupsd_client_t *con, /* I - Client connection */ cupsdSetStringf(&(job->ccname), "KRB5CCNAME=FILE:%s", krb5_cc_get_name(krb_context, ccache)); krb5_cc_close(krb_context, ccache); +# endif /* !__APPLE__ } #endif /* HAVE_GSSAPI && HAVE_KRB5_H */ diff --git a/scheduler/main.c b/scheduler/main.c index c4d269a9a..066d6e102 100644 --- a/scheduler/main.c +++ b/scheduler/main.c @@ -357,8 +357,8 @@ main(int argc, /* I - Number of command-line args */ getrlimit(RLIMIT_NOFILE, &limit); - if (limit.rlim_max > CUPS_MAX_FDS) - MaxFDs = CUPS_MAX_FDS; + if (limit.rlim_max > FD_SETSIZE) + MaxFDs = FD_SETSIZE; else MaxFDs = limit.rlim_max;