From: Ralph Boehme Date: Fri, 27 Sep 2013 14:18:34 +0000 (+0200) Subject: Disable Kerberos UAM if AFP service principal name can't be evaluated X-Git-Url: https://arthur.barton.de/gitweb/?p=netatalk.git;a=commitdiff_plain;h=53e7f8a164ca820d5dfe41b7d39ffba8fb056a72 Disable Kerberos UAM if AFP service principal name can't be evaluated Fixes bug #531. --- diff --git a/NEWS b/NEWS index 4a008b67..cbf24f7f 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,8 @@ Changes in 3.0.6 another filesystem results in a crash of the afpd process and the save to fail. This happens only if the option "follow symlinks" is enabled. Bug #532. +* FIX: Disable Kerberos UAM if AFP service principal name can't be + evaluated. Fixes bug #531. Changes in 3.0.5 ================ diff --git a/etc/afpd/afp_config.c b/etc/afpd/afp_config.c index 94adb993..14959343 100644 --- a/etc/afpd/afp_config.c +++ b/etc/afpd/afp_config.c @@ -93,7 +93,7 @@ int configinit(AFPObj *obj) int family, s; static char interfaddr[NI_MAXHOST]; - auth_load(obj->options.uampath, obj->options.uamlist); + auth_load(obj, obj->options.uampath, obj->options.uamlist); set_signature(&obj->options); #ifdef HAVE_LDAP acl_ldap_freeconfig(); diff --git a/etc/afpd/auth.c b/etc/afpd/auth.c index 3759538f..27e678c2 100644 --- a/etc/afpd/auth.c +++ b/etc/afpd/auth.c @@ -1017,7 +1017,7 @@ int auth_register(const int type, struct uam_obj *uam) } /* load all of the modules */ -int auth_load(const char *path, const char *list) +int auth_load(AFPObj *obj, const char *path, const char *list) { char name[MAXPATHLEN + 1], buf[MAXPATHLEN + 1], *p; struct uam_mod *mod; @@ -1044,7 +1044,7 @@ int auth_load(const char *path, const char *list) if ((stat(name, &st) == 0) && (mod = uam_load(name, p))) { */ if (stat(name, &st) == 0) { - if ((mod = uam_load(name, p))) { + if ((mod = uam_load(obj, name, p))) { uam_attach(&uam_modules, mod); LOG(log_debug, logtype_afpd, "uam: %s loaded", p); } else { diff --git a/etc/afpd/status.c b/etc/afpd/status.c index 35e08849..edf74466 100644 --- a/etc/afpd/status.c +++ b/etc/afpd/status.c @@ -311,39 +311,6 @@ static size_t status_netaddress(char *data, int *servoffset, return (data - begin); } -static bool append_directoryname(char **pdata, - size_t offset, - size_t *size, - char* DirectoryNamesCount, - size_t len, - char *principal) -{ - if (sizeof(uint8_t) + len > maxstatuslen - offset - *size) { - LOG(log_error, logtype_afpd, - "status:DirectoryNames: out of space for principal '%s' (len=%d)", - principal, len); - return false; - } else if (len > 255) { - LOG(log_error, logtype_afpd, - "status:DirectoryNames: principal '%s' (len=%d) too long (max=255)", - principal, len); - return false; - } - - LOG(log_info, logtype_afpd, - "DirectoryNames[%d]=%s", - *DirectoryNamesCount, principal); - - *DirectoryNamesCount += 1; - char *data = *pdata; - *data++ = len; - strncpy(data, principal, len); - - *pdata += len + 1; - *size += sizeof(uint8_t) + len; - - return true; -} /** * DirectoryNamesCount offset: uint16_t @@ -354,7 +321,7 @@ static bool append_directoryname(char **pdata, static size_t status_directorynames(char *data, int *diroffset, const DSI *dsi _U_, - const struct afp_options *options _U_) + const struct afp_options *options) { char *begin = data; uint16_t offset; @@ -363,130 +330,14 @@ static size_t status_directorynames(char *data, offset = ntohs(offset); data += offset; - char *DirectoryNamesCount = data++; - size_t size = sizeof(uint8_t); - *DirectoryNamesCount = 0; - - if (!uam_gss_enabled()) - goto offset_calc; - -#ifdef HAVE_KERBEROS - krb5_context context; - krb5_error_code ret; - const char *error_msg; - - if (krb5_init_context(&context)) { - LOG(log_error, logtype_afpd, - "status:DirectoryNames failed to intialize a krb5_context"); - goto offset_calc; - } - - krb5_keytab keytab; - if ((ret = krb5_kt_default(context, &keytab))) - goto krb5_error; - - // figure out which service principal to use - krb5_keytab_entry entry; - char *principal; - if (options->k5service && options->fqdn && options->k5realm) { - LOG(log_debug, logtype_afpd, - "status:DirectoryNames: using service principal specified in options"); - - krb5_principal service_principal; - if ((ret = krb5_build_principal(context, - &service_principal, - strlen(options->k5realm), - options->k5realm, - options->k5service, - options->fqdn, - NULL))) - goto krb5_error; - - // try to get the given service principal from keytab - ret = krb5_kt_get_entry(context, - keytab, - service_principal, - 0, // kvno - wildcard - 0, // enctype - wildcard - &entry); - if (ret == KRB5_KT_NOTFOUND) { - krb5_unparse_name(context, service_principal, &principal); - LOG(log_error, logtype_afpd, - "status:DirectoryNames: specified service principal '%s' not found in keytab", - principal); - // XXX: should this be krb5_xfree? -#ifdef HAVE_KRB5_FREE_UNPARSED_NAME - krb5_free_unparsed_name(context, principal); -#else - krb5_xfree(principal); -#endif - goto krb5_cleanup; - } - krb5_free_principal(context, service_principal); - if (ret) - goto krb5_error; + if (!uam_gss_enabled() || !options->k5principal) { + *data = 0; + data++; } else { - LOG(log_debug, logtype_afpd, - "status:DirectoryNames: using first entry from keytab as service principal"); - - krb5_kt_cursor cursor; - if ((ret = krb5_kt_start_seq_get(context, keytab, &cursor))) - goto krb5_error; - - ret = krb5_kt_next_entry(context, keytab, &entry, &cursor); - krb5_kt_end_seq_get(context, keytab, &cursor); - if (ret) - goto krb5_error; + memcpy(data, options->k5principal, options->k5principal_buflen); + data += options->k5principal_buflen; } - krb5_unparse_name(context, entry.principal, &principal); -#ifdef HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS - krb5_free_keytab_entry_contents(context, &entry); -#elif defined(HAVE_KRB5_KT_FREE_ENTRY) - krb5_kt_free_entry(context, &entry); -#endif - append_directoryname(&data, - offset, - &size, - DirectoryNamesCount, - strlen(principal), - principal); - - free(principal); - goto krb5_cleanup; - -krb5_error: - if (ret) { - error_msg = krb5_get_error_message(context, ret); - LOG(log_note, logtype_afpd, "Can't get principal from default keytab: %s", - (char *)error_msg); -#ifdef HAVE_KRB5_FREE_ERROR_MESSAGE - krb5_free_error_message(context, error_msg); -#else - krb5_xfree(error_msg); -#endif - } - -krb5_cleanup: - krb5_kt_close(context, keytab); - krb5_free_context(context); -#else - if (!options->k5service || !options->fqdn || !options->k5realm) - goto offset_calc; - - char principal[255]; - size_t len = snprintf(principal, sizeof(principal), "%s/%s@%s", - options->k5service, options->fqdn, options->k5realm); - - append_directoryname(&data, - offset, - &size, - DirectoryNamesCount, - strlen(principal), - principal); -#endif // HAVE_KERBEROS - -offset_calc: /* Calculate and store offset for UTF8ServerName */ *diroffset += sizeof(uint16_t); offset = htons(data - begin); diff --git a/etc/afpd/uam.c b/etc/afpd/uam.c index 208c9ff2..04e54cbb 100644 --- a/etc/afpd/uam.c +++ b/etc/afpd/uam.c @@ -46,7 +46,7 @@ /* --- server uam functions -- */ /* uam_load. uams must have a uam_setup function. */ -struct uam_mod *uam_load(const char *path, const char *name) +struct uam_mod *uam_load(AFPObj *obj, const char *path, const char *name) { char buf[MAXPATHLEN + 1], *p; struct uam_mod *mod; @@ -82,7 +82,7 @@ struct uam_mod *uam_load(const char *path, const char *name) /* version check would go here */ if (!mod->uam_fcn->uam_setup || - ((*mod->uam_fcn->uam_setup)(name) < 0)) { + ((*mod->uam_fcn->uam_setup)(obj, name) < 0)) { LOG(log_error, logtype_afpd, "uam_load(%s): uam_setup failed", name); goto uam_load_err; } diff --git a/etc/afpd/uam_auth.h b/etc/afpd/uam_auth.h index a553ffab..4eb3bf04 100644 --- a/etc/afpd/uam_auth.h +++ b/etc/afpd/uam_auth.h @@ -51,11 +51,11 @@ struct uam_obj { (a)->uam_next->uam_prev = (a)->uam_prev; \ } while (0) -extern struct uam_mod *uam_load (const char *, const char *); +extern struct uam_mod *uam_load (AFPObj *, const char *, const char *); extern void uam_unload (struct uam_mod *); /* auth.c */ -int auth_load (const char *, const char *); +int auth_load (AFPObj *, const char *, const char *); int auth_register (const int, struct uam_obj *); #define auth_unregister(a) uam_detach(a) struct uam_obj *auth_uamfind (const int, const char *, const int); diff --git a/etc/uams/uams_dhx2_pam.c b/etc/uams/uams_dhx2_pam.c index 76e9143f..8857759d 100644 --- a/etc/uams/uams_dhx2_pam.c +++ b/etc/uams/uams_dhx2_pam.c @@ -923,7 +923,7 @@ static int dhx2_changepw(void *obj _U_, char *uname, return ret; } -static int uam_setup(const char *path) +static int uam_setup(void *obj, const char *path) { if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", pam_login, pam_logincont, pam_logout, pam_login_ext) < 0) diff --git a/etc/uams/uams_dhx2_passwd.c b/etc/uams/uams_dhx2_passwd.c index a2737d0f..b74cc124 100644 --- a/etc/uams/uams_dhx2_passwd.c +++ b/etc/uams/uams_dhx2_passwd.c @@ -603,7 +603,7 @@ static int passwd_logincont(void *obj, struct passwd **uam_pwd, return ret; } -static int uam_setup(const char *path) +static int uam_setup(void *obj, const char *path) { if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", passwd_login, passwd_logincont, NULL, passwd_login_ext) < 0) diff --git a/etc/uams/uams_dhx_pam.c b/etc/uams/uams_dhx_pam.c index bc19c785..631f84d9 100644 --- a/etc/uams/uams_dhx_pam.c +++ b/etc/uams/uams_dhx_pam.c @@ -719,7 +719,7 @@ static int pam_changepw(void *obj, char *username, } -static int uam_setup(const char *path) +static int uam_setup(void *obj, const char *path) { if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHCAST128", pam_login, pam_logincont, pam_logout, pam_login_ext) < 0) diff --git a/etc/uams/uams_dhx_passwd.c b/etc/uams/uams_dhx_passwd.c index d390f76b..e407d9d0 100644 --- a/etc/uams/uams_dhx_passwd.c +++ b/etc/uams/uams_dhx_passwd.c @@ -371,7 +371,7 @@ static int passwd_logincont(void *obj, struct passwd **uam_pwd, } -static int uam_setup(const char *path) +static int uam_setup(void *obj, const char *path) { if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHCAST128", passwd_login, passwd_logincont, NULL, passwd_login_ext) < 0) diff --git a/etc/uams/uams_gss.c b/etc/uams/uams_gss.c index 29ea36c0..28ed7444 100644 --- a/etc/uams/uams_gss.c +++ b/etc/uams/uams_gss.c @@ -21,6 +21,7 @@ #include #include #include +#include /* Kerberos includes */ #ifdef HAVE_GSSAPI_GSSAPI_H @@ -29,6 +30,14 @@ #include #endif // HAVE_GSSAPI_GSSAPI_H +#ifdef HAVE_KERBEROS +#ifdef HAVE_KRB5_KRB5_H +#include +#else +#include +#endif /* HAVE_KRB5_KRB5_H */ +#endif /* HAVE_KERBEROS */ + #define LOG_UAMS(log_level, ...) \ LOG(log_level, logtype_uams, __VA_ARGS__) @@ -479,8 +488,140 @@ static int gss_login_ext(void *obj, return gss_login(obj, uam_pwd, ibuf, ibuflen, rbuf, rbuflen); } -static int uam_setup(const char *path) +static int set_principal(AFPObj *obj, char *principal) { + size_t len = strlen(principal); + + if (len > 255) { + LOG(log_error, logtype_afpd, "set_principal: principal '%s' too long (max=255)", principal, len); + return -1; + } + + /* We store: 1 byte number principals (1) + 1 byte principal name length + zero terminated principal */ + if ((obj->options.k5principal = malloc(len + 3)) == NULL) { + LOG(log_error, logtype_afpd, "set_principal: OOM"); + return -1; + } + + LOG(log_info, logtype_afpd, "Using AFP Kerberos service principal name: %s", principal); + + obj->options.k5principal[0] = 1; + obj->options.k5principal[1] = (unsigned char)len; + obj->options.k5principal_buflen = len + 2; + strncpy(obj->options.k5principal + 2, principal, len); + + return 0; +} + +static int gss_create_principal(AFPObj *obj) +{ + int rv = -1; +#ifdef HAVE_KERBEROS + krb5_context context; + krb5_error_code ret; + const char *error_msg; + krb5_keytab keytab; + krb5_keytab_entry entry; + krb5_principal service_principal; + char *principal; + krb5_kt_cursor cursor; + + if (krb5_init_context(&context)) { + LOG(log_error, logtype_afpd, "gss_create_principal: failed to intialize a krb5_context"); + goto exit; + } + if ((ret = krb5_kt_default(context, &keytab))) + goto krb5_error; + + if (obj->options.k5service && obj->options.fqdn && obj->options.k5realm) { + LOG(log_debug, logtype_afpd, "gss_create_principal: using service principal specified in options"); + + if ((ret = krb5_build_principal(context, + &service_principal, + strlen(obj->options.k5realm), + obj->options.k5realm, + obj->options.k5service, + obj->options.fqdn, + NULL))) + goto krb5_error; + + if ((ret = krb5_kt_get_entry(context, + keytab, + service_principal, + 0, // kvno - wildcard + 0, // enctype - wildcard + &entry)) == KRB5_KT_NOTFOUND) { + krb5_unparse_name(context, service_principal, &principal); + LOG(log_error, logtype_afpd, "gss_create_principal: specified service principal '%s' not found in keytab", principal); +#ifdef HAVE_KRB5_FREE_UNPARSED_NAME + krb5_free_unparsed_name(context, principal); +#else + krb5_xfree(principal); +#endif + goto krb5_cleanup; + } + krb5_free_principal(context, service_principal); + if (ret) + goto krb5_error; + } else { + LOG(log_debug, logtype_afpd, "gss_create_principal: using first entry from keytab as service principal"); + if ((ret = krb5_kt_start_seq_get(context, keytab, &cursor))) + goto krb5_error; + ret = krb5_kt_next_entry(context, keytab, &entry, &cursor); + krb5_kt_end_seq_get(context, keytab, &cursor); + if (ret) + goto krb5_error; + } + + krb5_unparse_name(context, entry.principal, &principal); +#ifdef HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS + krb5_free_keytab_entry_contents(context, &entry); +#elif defined(HAVE_KRB5_KT_FREE_ENTRY) + krb5_kt_free_entry(context, &entry); +#endif + set_principal(obj, principal); + free(principal); + rv = 0; + goto krb5_cleanup; + +krb5_error: + if (ret) { + error_msg = krb5_get_error_message(context, ret); + LOG(log_note, logtype_afpd, "Can't get principal from default keytab: %s", + (char *)error_msg); +#ifdef HAVE_KRB5_FREE_ERROR_MESSAGE + krb5_free_error_message(context, error_msg); +#else + krb5_xfree(error_msg); +#endif + } + +krb5_cleanup: + krb5_kt_close(context, keytab); + krb5_free_context(context); + +#else /* ! HAVE_KERBEROS */ + + if (!options->k5service || !options->fqdn || !options->k5realm) + goto exit; + + char principal[255]; + size_t len = snprintf(principal, sizeof(principal), "%s/%s@%s", + options->k5service, options->fqdn, options->k5realm); + (void)set_principal(&obj, principal); + rv = 0; +#endif /* HAVE_KERBEROS */ + +exit: + return rv; +} + +static int uam_setup(void *handle, const char *path) +{ + AFPObj *obj = (AFPObj *)handle; + if (gss_create_principal(obj) != 0) + return -1; + return uam_register(UAM_SERVER_LOGIN_EXT, path, "Client Krb v2", gss_login, gss_logincont, gss_logout, gss_login_ext); } diff --git a/etc/uams/uams_guest.c b/etc/uams/uams_guest.c index 82ee43ef..baaf9eb2 100644 --- a/etc/uams/uams_guest.c +++ b/etc/uams/uams_guest.c @@ -121,7 +121,7 @@ static int noauth_printer(char *start, char *stop, char *username, struct papfil } -static int uam_setup(const char *path) +static int uam_setup(void *handle, const char *path) { if (uam_register(UAM_SERVER_LOGIN_EXT, path, "No User Authent", noauth_login, NULL, NULL, noauth_login_ext) < 0) diff --git a/etc/uams/uams_pam.c b/etc/uams/uams_pam.c index b490c2ee..da5be621 100644 --- a/etc/uams/uams_pam.c +++ b/etc/uams/uams_pam.c @@ -445,7 +445,7 @@ static int pam_printer(char *start, char *stop, char *username, struct papfile * } -static int uam_setup(const char *path) +static int uam_setup(void *obj, const char *path) { if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Cleartxt Passwrd", pam_login, NULL, pam_logout, pam_login_ext) < 0) diff --git a/etc/uams/uams_passwd.c b/etc/uams/uams_passwd.c index bec45a36..9cd738cc 100644 --- a/etc/uams/uams_passwd.c +++ b/etc/uams/uams_passwd.c @@ -353,7 +353,7 @@ static int passwd_printer(char *start, char *stop, char *username, struct papfil return(0); } -static int uam_setup(const char *path) +static int uam_setup(void *obj, const char *path) { if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Cleartxt Passwrd", passwd_login, NULL, NULL, passwd_login_ext) < 0) diff --git a/etc/uams/uams_pgp.c b/etc/uams/uams_pgp.c index 6a299440..679a1601 100644 --- a/etc/uams/uams_pgp.c +++ b/etc/uams/uams_pgp.c @@ -174,7 +174,7 @@ static int pgp_logincont(void *obj, struct passwd **uam_pwd, } -static int uam_setup(const char *path) +static int uam_setup(void *obj, const char *path) { if (uam_register(UAM_SERVER_LOGIN, path, "PGPuam 1.0", pgp_login, pgp_logincont, NULL) < 0) diff --git a/etc/uams/uams_randnum.c b/etc/uams/uams_randnum.c index 22687fcc..50ce6ce5 100644 --- a/etc/uams/uams_randnum.c +++ b/etc/uams/uams_randnum.c @@ -531,7 +531,7 @@ static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd, return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen)); } -static int uam_setup(const char *path) +static int uam_setup(void *obj, const char *path) { if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange", randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0) diff --git a/include/atalk/globals.h b/include/atalk/globals.h index e1b5454b..9095e2e3 100644 --- a/include/atalk/globals.h +++ b/include/atalk/globals.h @@ -105,6 +105,8 @@ struct afp_options { char *signatureopt; unsigned char signature[16]; char *k5service, *k5realm, *k5keytab; + size_t k5principal_buflen; + char *k5principal; char *unixcodepage, *maccodepage, *volcodepage; charset_t maccharset, unixcharset; mode_t umask; diff --git a/include/atalk/uam.h b/include/atalk/uam.h index dcb32e99..d2890e75 100644 --- a/include/atalk/uam.h +++ b/include/atalk/uam.h @@ -60,7 +60,7 @@ * support is braindead. it also allows me to do a little versioning. */ struct uam_export { int uam_type, uam_version; - int (*uam_setup)(const char *); + int (*uam_setup)(void *, const char *); void (*uam_cleanup)(void); }; diff --git a/libatalk/util/netatalk_conf.c b/libatalk/util/netatalk_conf.c index 503a423c..9ed1c776 100644 --- a/libatalk/util/netatalk_conf.c +++ b/libatalk/util/netatalk_conf.c @@ -1983,6 +1983,8 @@ void afp_config_free(AFPObj *obj) CONFIG_ARG_FREE(obj->options.k5service); if (obj->options.k5realm) CONFIG_ARG_FREE(obj->options.k5realm); + if (obj->options.k5principal) + CONFIG_ARG_FREE(obj->options.k5principal); if (obj->options.listen) CONFIG_ARG_FREE(obj->options.listen); if (obj->options.interfaces)