aboutsummaryrefslogtreecommitdiffstats
path: root/kpasswd/kpasswdd.c
diff options
context:
space:
mode:
Diffstat (limited to 'kpasswd/kpasswdd.c')
-rw-r--r--kpasswd/kpasswdd.c282
1 files changed, 144 insertions, 138 deletions
diff --git a/kpasswd/kpasswdd.c b/kpasswd/kpasswdd.c
index 36a70c832521..967bf9383b34 100644
--- a/kpasswd/kpasswdd.c
+++ b/kpasswd/kpasswdd.c
@@ -123,21 +123,26 @@ make_result (krb5_data *data,
uint16_t result_code,
const char *expl)
{
- char *str;
- krb5_data_zero (data);
-
- data->length = asprintf (&str,
- "%c%c%s",
- (result_code >> 8) & 0xFF,
- result_code & 0xFF,
- expl);
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) goto out;
+ ret = krb5_store_uint16(sp, result_code);
+ if (ret) goto out;
+ ret = krb5_store_stringz(sp, expl);
+ if (ret) goto out;
+ ret = krb5_storage_to_data(sp, data);
+ if (ret) goto out;
+ krb5_storage_free(sp);
- if (str == NULL) {
- krb5_warnx (context, "Out of memory generating error reply");
- return 1;
- }
- data->data = str;
return 0;
+ out:
+ if (sp)
+ krb5_storage_free(sp);
+
+ krb5_warnx (context, "Out of memory generating error reply");
+ return 1;
}
static void
@@ -293,6 +298,7 @@ change (krb5_auth_context auth_context,
if (chpw.targname) {
krb5_principal_data princ;
+ memset(&princ, 0, sizeof (princ));
princ.name = *chpw.targname;
princ.realm = *chpw.targrealm;
if (princ.realm == NULL) {
@@ -401,7 +407,7 @@ change (krb5_auth_context auth_context,
tmp = pwd_data->data;
tmp[pwd_data->length - 1] = '\0';
- ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp);
+ ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, 1, tmp);
krb5_free_data (context, pwd_data);
pwd_data = NULL;
if (ret) {
@@ -430,7 +436,6 @@ out:
static int
verify (krb5_auth_context *auth_context,
- krb5_realm *realms,
krb5_keytab keytab,
krb5_ticket **ticket,
krb5_data *out_data,
@@ -446,7 +451,9 @@ verify (krb5_auth_context *auth_context,
uint16_t pkt_len, pkt_ver, ap_req_len;
krb5_data ap_req_data;
krb5_data krb_priv_data;
- krb5_realm *r;
+ krb5_const_realm client_realm;
+ krb5_principal sprinc;
+ int same;
/*
* Only send an error reply if the request passes basic length
@@ -495,47 +502,40 @@ verify (krb5_auth_context *auth_context,
return 1;
}
- /* verify realm and principal */
- for (r = realms; *r != NULL; r++) {
- krb5_principal principal;
- krb5_boolean same;
-
- ret = krb5_make_principal (context,
- &principal,
- *r,
- "kadmin",
- "changepw",
- NULL);
- if (ret)
- krb5_err (context, 1, ret, "krb5_make_principal");
-
- same = krb5_principal_compare(context, principal, (*ticket)->server);
- krb5_free_principal(context, principal);
- if (same == TRUE)
- break;
- }
- if (*r == NULL) {
- char *str;
- krb5_unparse_name(context, (*ticket)->server, &str);
- krb5_warnx (context, "client used not valid principal %s", str);
- free(str);
- reply_error (NULL, s, sa, sa_size, ret, 1,
- "Bad request");
+ if (!(*ticket)->ticket.flags.initial) {
+ krb5_warnx(context, "initial flag not set");
+ reply_error((*ticket)->server->realm, s, sa, sa_size, ret, 1,
+ "Bad request");
goto out;
}
- if (strcmp((*ticket)->server->realm, (*ticket)->client->realm) != 0) {
- krb5_warnx (context, "server realm (%s) not same a client realm (%s)",
- (*ticket)->server->realm, (*ticket)->client->realm);
- reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
- "Bad request");
- goto out;
- }
+ /*
+ * The service principal must be kadmin/changepw@CLIENT-REALM, there
+ * is no reason to require the KDC's default realm(s) to be the same
+ * as the realm(s) it serves. The only potential issue is when a KDC
+ * is a master for realm A and a slave for realm B, in which case it
+ * should not accept requests to change passwords for realm B, these
+ * should be sent to realm B's master. This same issue is present in
+ * the checks that only accepted local realms, there is no new risk.
+ */
- if (!(*ticket)->ticket.flags.initial) {
- krb5_warnx (context, "initial flag not set");
- reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
- "Bad request");
+ client_realm = krb5_principal_get_realm(context, (*ticket)->client);
+ ret = krb5_make_principal(context, &sprinc, client_realm,
+ "kadmin", "changepw", NULL);
+ if (ret)
+ goto out;
+ same = krb5_principal_compare(context, sprinc, (*ticket)->server);
+ krb5_free_principal(context, sprinc);
+
+ if (!same) {
+ char *sname;
+
+ if (krb5_unparse_name(context, (*ticket)->server, &sname) != 0)
+ sname = NULL;
+ krb5_warnx(context, "Invalid kpasswd service principal %s",
+ sname ? sname : "<enomem>");
+ free(sname);
+ reply_error(NULL, s, sa, sa_size, ret, 1, "Bad request");
goto out;
}
krb_priv_data.data = msg + 6 + ap_req_len;
@@ -576,8 +576,7 @@ out:
}
static void
-process (krb5_realm *realms,
- krb5_keytab keytab,
+process (krb5_keytab keytab,
int s,
krb5_address *this_addr,
struct sockaddr *sa,
@@ -616,7 +615,7 @@ process (krb5_realm *realms,
goto out;
}
- if (verify (&auth_context, realms, keytab, &ticket, &out_data,
+ if (verify (&auth_context, keytab, &ticket, &out_data,
&version, s, sa, sa_size, msg, len, &other_addr) == 0)
{
/*
@@ -647,74 +646,109 @@ out:
krb5_auth_con_free(context, auth_context);
}
+static const char *check_library = NULL;
+static const char *check_function = NULL;
+static getarg_strings policy_libraries = { 0, NULL };
+static char sHDB[] = "HDBGET:";
+static char *keytab_str = sHDB;
+static char *realm_str;
+static int version_flag;
+static int help_flag;
+static int detach_from_console;
+static int daemon_child = -1;
+static char *port_str;
+static char *config_file;
+
+struct getargs args[] = {
+#ifdef HAVE_DLOPEN
+ { "check-library", 0, arg_string, &check_library,
+ "library to load password check function from", "library" },
+ { "check-function", 0, arg_string, &check_function,
+ "password check function to load", "function" },
+ { "policy-libraries", 0, arg_strings, &policy_libraries,
+ "password check function to load", "function" },
+#endif
+ { "addresses", 0, arg_strings, &addresses_str,
+ "addresses to listen on", "list of addresses" },
+ { "detach", 0, arg_flag, &detach_from_console,
+ "detach from console", NULL },
+ { "daemon-child", 0 , arg_integer, &daemon_child,
+ "private argument, do not use", NULL },
+ { "keytab", 'k', arg_string, &keytab_str,
+ "keytab to get authentication key from", "kspec" },
+ { "config-file", 'c', arg_string, &config_file, NULL, NULL },
+ { "realm", 'r', arg_string, &realm_str, "default realm", "realm" },
+ { "port", 'p', arg_string, &port_str, "port", NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+int num_args = sizeof(args) / sizeof(args[0]);
+
static int
-doit (krb5_keytab keytab, int port)
+doit(krb5_keytab keytab, int port)
{
krb5_error_code ret;
int *sockets;
int maxfd;
- krb5_realm *realms;
krb5_addresses addrs;
unsigned n, i;
fd_set real_fdset;
struct sockaddr_storage __ss;
struct sockaddr *sa = (struct sockaddr *)&__ss;
- ret = krb5_get_default_realms(context, &realms);
- if (ret)
- krb5_err (context, 1, ret, "krb5_get_default_realms");
-
if (explicit_addresses.len) {
addrs = explicit_addresses;
} else {
- ret = krb5_get_all_server_addrs (context, &addrs);
+ ret = krb5_get_all_server_addrs(context, &addrs);
if (ret)
- krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
+ krb5_err(context, 1, ret, "krb5_get_all_server_addrs");
}
n = addrs.len;
- sockets = malloc (n * sizeof(*sockets));
+ sockets = malloc(n * sizeof(*sockets));
if (sockets == NULL)
- krb5_errx (context, 1, "out of memory");
+ krb5_errx(context, 1, "out of memory");
maxfd = -1;
FD_ZERO(&real_fdset);
for (i = 0; i < n; ++i) {
krb5_socklen_t sa_size = sizeof(__ss);
- krb5_addr2sockaddr (context, &addrs.val[i], sa, &sa_size, port);
+ krb5_addr2sockaddr(context, &addrs.val[i], sa, &sa_size, port);
- sockets[i] = socket (sa->sa_family, SOCK_DGRAM, 0);
+ sockets[i] = socket(__ss.ss_family, SOCK_DGRAM, 0);
if (sockets[i] < 0)
- krb5_err (context, 1, errno, "socket");
- if (bind (sockets[i], sa, sa_size) < 0) {
+ krb5_err(context, 1, errno, "socket");
+ if (bind(sockets[i], sa, sa_size) < 0) {
char str[128];
size_t len;
int save_errno = errno;
- ret = krb5_print_address (&addrs.val[i], str, sizeof(str), &len);
+ ret = krb5_print_address(&addrs.val[i], str, sizeof(str), &len);
if (ret)
strlcpy(str, "unknown address", sizeof(str));
- krb5_warn (context, save_errno, "bind(%s)", str);
+ krb5_warn(context, save_errno, "bind(%s)", str);
continue;
}
- maxfd = max (maxfd, sockets[i]);
+ maxfd = max(maxfd, sockets[i]);
if (maxfd >= FD_SETSIZE)
- krb5_errx (context, 1, "fd too large");
+ krb5_errx(context, 1, "fd too large");
FD_SET(sockets[i], &real_fdset);
}
if (maxfd == -1)
- krb5_errx (context, 1, "No sockets!");
+ krb5_errx(context, 1, "No sockets!");
- while(exit_flag == 0) {
+ roken_detach_finish(NULL, daemon_child);
+
+ while (exit_flag == 0) {
krb5_ssize_t retx;
fd_set fdset = real_fdset;
- retx = select (maxfd + 1, &fdset, NULL, NULL, NULL);
+ retx = select(maxfd + 1, &fdset, NULL, NULL, NULL);
if (retx < 0) {
if (errno == EINTR)
continue;
else
- krb5_err (context, 1, errno, "select");
+ krb5_err(context, 1, errno, "select");
}
for (i = 0; i < n; ++i)
if (FD_ISSET(sockets[i], &fdset)) {
@@ -724,13 +758,13 @@ doit (krb5_keytab keytab, int port)
retx = recvfrom(sockets[i], buf, sizeof(buf), 0,
sa, &addrlen);
if (retx < 0) {
- if(errno == EINTR)
+ if (errno == EINTR)
break;
else
- krb5_err (context, 1, errno, "recvfrom");
+ krb5_err(context, 1, errno, "recvfrom");
}
- process (realms, keytab, sockets[i],
+ process(keytab, sockets[i],
&addrs.val[i],
sa, addrlen,
buf, retx);
@@ -741,9 +775,8 @@ doit (krb5_keytab keytab, int port)
close(sockets[i]);
free(sockets);
- krb5_free_addresses (context, &addrs);
- krb5_free_host_realm (context, realms);
- krb5_free_context (context);
+ krb5_free_addresses(context, &addrs);
+ krb5_free_context(context);
return 0;
}
@@ -753,58 +786,31 @@ sigterm(int sig)
exit_flag = 1;
}
-static const char *check_library = NULL;
-static const char *check_function = NULL;
-static getarg_strings policy_libraries = { 0, NULL };
-static char sHDB[] = "HDB:";
-static char *keytab_str = sHDB;
-static char *realm_str;
-static int version_flag;
-static int help_flag;
-static char *port_str;
-static char *config_file;
-
-struct getargs args[] = {
-#ifdef HAVE_DLOPEN
- { "check-library", 0, arg_string, &check_library,
- "library to load password check function from", "library" },
- { "check-function", 0, arg_string, &check_function,
- "password check function to load", "function" },
- { "policy-libraries", 0, arg_strings, &policy_libraries,
- "password check function to load", "function" },
-#endif
- { "addresses", 0, arg_strings, &addresses_str,
- "addresses to listen on", "list of addresses" },
- { "keytab", 'k', arg_string, &keytab_str,
- "keytab to get authentication key from", "kspec" },
- { "config-file", 'c', arg_string, &config_file, NULL, NULL },
- { "realm", 'r', arg_string, &realm_str, "default realm", "realm" },
- { "port", 'p', arg_string, &port_str, "port", NULL },
- { "version", 0, arg_flag, &version_flag, NULL, NULL },
- { "help", 0, arg_flag, &help_flag, NULL, NULL }
-};
-int num_args = sizeof(args) / sizeof(args[0]);
-
int
-main (int argc, char **argv)
+main(int argc, char **argv)
{
krb5_keytab keytab;
krb5_error_code ret;
char **files;
int port, i;
+ int aret;
krb5_program_setup(&context, argc, argv, args, num_args, NULL);
- if(help_flag)
+ if (help_flag)
krb5_std_usage(0, args, num_args);
- if(version_flag) {
+
+ if (version_flag) {
print_version(NULL);
exit(0);
}
+ if (detach_from_console > 0 && daemon_child == -1)
+ roken_detach_prep(argc, argv, "--daemon-child");
+
if (config_file == NULL) {
- asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
- if (config_file == NULL)
+ aret = asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
+ if (aret == -1)
errx(1, "out of memory");
}
@@ -817,37 +823,37 @@ main (int argc, char **argv)
if (ret)
krb5_err(context, 1, ret, "reading configuration files");
- if(realm_str)
+ if (realm_str)
krb5_set_default_realm(context, realm_str);
- krb5_openlog (context, "kpasswdd", &log_facility);
+ krb5_openlog(context, "kpasswdd", &log_facility);
krb5_set_warn_dest(context, log_facility);
if (port_str != NULL) {
- struct servent *s = roken_getservbyname (port_str, "udp");
+ struct servent *s = roken_getservbyname(port_str, "udp");
if (s != NULL)
port = s->s_port;
else {
char *ptr;
- port = strtol (port_str, &ptr, 10);
+ port = strtol(port_str, &ptr, 10);
if (port == 0 && ptr == port_str)
- krb5_errx (context, 1, "bad port `%s'", port_str);
+ krb5_errx(context, 1, "bad port `%s'", port_str);
port = htons(port);
}
} else
- port = krb5_getportbyname (context, "kpasswd", "udp", KPASSWD_PORT);
+ port = krb5_getportbyname(context, "kpasswd", "udp", KPASSWD_PORT);
- ret = krb5_kt_register(context, &hdb_kt_ops);
- if(ret)
+ ret = krb5_kt_register(context, &hdb_get_kt_ops);
+ if (ret)
krb5_err(context, 1, ret, "krb5_kt_register");
ret = krb5_kt_resolve(context, keytab_str, &keytab);
- if(ret)
+ if (ret)
krb5_err(context, 1, ret, "%s", keytab_str);
- kadm5_setup_passwd_quality_check (context, check_library, check_function);
+ kadm5_setup_passwd_quality_check(context, check_library, check_function);
for (i = 0; i < policy_libraries.num_strings; i++) {
ret = kadm5_add_passwd_quality_verifier(context,
@@ -866,16 +872,16 @@ main (int argc, char **argv)
int j;
for (j = 0; j < addresses_str.num_strings; ++j)
- add_one_address (addresses_str.strings[j], j == 0);
- free_getarg_strings (&addresses_str);
+ add_one_address(addresses_str.strings[j], j == 0);
+ free_getarg_strings(&addresses_str);
} else {
- char **foo = krb5_config_get_strings (context, NULL,
+ char **foo = krb5_config_get_strings(context, NULL,
"kdc", "addresses", NULL);
if (foo != NULL) {
- add_one_address (*foo++, TRUE);
+ add_one_address(*foo++, TRUE);
while (*foo)
- add_one_address (*foo++, FALSE);
+ add_one_address(*foo++, FALSE);
}
}
@@ -895,7 +901,7 @@ main (int argc, char **argv)
signal(SIGTERM, sigterm);
#endif
- pidfile(NULL);
+ rk_pidfile(NULL);
- return doit (keytab, port);
+ return doit(keytab, port);
}