mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2026-09-03 20:52:56 -05:00
commit before pulling new dir layout
misc patches to ACLs the example DNS zone and other stuff misc fixes to the krb instance current kpasswd code
This commit is contained in:
+6
-1
@@ -5,12 +5,17 @@ krb5-server
|
||||
fedora-ds-base
|
||||
openldap-clients
|
||||
krb5-server-ldap
|
||||
cyrus-sasl-gssapi
|
||||
|
||||
|
||||
Installation example:
|
||||
|
||||
TEMPORARY: (until fedora ds scripts are fixed)
|
||||
please use the fedora-ds.init.patch under share/ to patch your init scripts before
|
||||
running ipa-server-install
|
||||
|
||||
cd ipa-install
|
||||
make install
|
||||
cd ..
|
||||
/usr/sbin/ipa-server-install -r FREEIPA.ORG -a rc1.freeipa.org -p freeipa -m ipafree
|
||||
/usr/sbin/ipa-server-install -u fds -r FREEIPA.ORG -p freeipa -m ipafree
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
$$ORIGIN $DOMAIN.
|
||||
$$TTL 86400
|
||||
@ IN SOA $DOMAIN. root.$DOMAIN. (
|
||||
01 ; serial (d. adams)
|
||||
3H ; refresh
|
||||
15M ; retry
|
||||
1W ; expiry
|
||||
1D ) ; minimum
|
||||
|
||||
IN NS $HOST
|
||||
$HOST IN A $IP
|
||||
;
|
||||
; ldap servers
|
||||
_ldap._tcp IN SRV 0 100 389 $HOST
|
||||
|
||||
;kerberos realm
|
||||
_kerberos IN TXT $REALM
|
||||
|
||||
; kerberos servers
|
||||
_kerberos._tcp IN SRV 0 100 88 $HOST
|
||||
_kerberos._udp IN SRV 0 100 88 $HOST
|
||||
_kerberos-master._tcp IN SRV 0 100 88 $HOST
|
||||
_kerberos-master._udp IN SRV 0 100 88 $HOST
|
||||
_kpasswd._tcp IN SRV 0 100 88 $HOST
|
||||
_kpasswd._udp IN SRV 0 100 88 $HOST
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
dn: $SUFFIX
|
||||
changetype: modify
|
||||
replace: aci
|
||||
aci: (targetattr!="userPassword || krbPrincipalKey ||sambaLMPassword || sambaNTPassword")(version 3.0; acl "Enable anonymous access"; allow (read, search, compare)userdn="ldap:///anyone";)
|
||||
aci: (targetattr!="userPassword || krbPrincipalKey ||sambaLMPassword || sambaNTPassword")(version 3.0; acl "Enable anonymous access"; allow (read, search, compare) userdn="ldap:///anyone";)
|
||||
aci: (targetattr="carLicense ||description ||displayName ||facsimileTelephoneNumber ||homePhone ||homePostalAddress ||initials ||jpegPhoto ||labeledURL ||mail ||mobile ||pager ||photo ||postOfficeBox ||postalAddress ||postalCode ||preferredDeliveryMethod ||preferredLanguage ||registeredAddress ||roomNumber | |secretary ||seeAlso ||st ||street ||telephoneNumber ||telexNumber ||title || userCertificate ||userPassword ||userSMIMECertificate ||x500UniqueIdentifier")(version 3.0; acl "Enable self write for common attributes"; allow (write) userdn="ldap:///self";)
|
||||
aci: (targetattr="krbPrincipalKey")(version 3.0; acl "KDC System Account"; allow(read, search,compare)userdn="ldap:///uid=kdc,cn=kerberos,$SUFFIX";)
|
||||
aci: (targetattr="krbPrincipalKey")(version 3.0; acl "KDC System Account"; allow (read, search, compare) userdn="ldap:///uid=kdc,cn=kerberos,$SUFFIX";)
|
||||
aci: (targetattr="*")(version 3.0; acl "Directory Administrators can manage all entries"; allow(all)groupdn="ldap:///cn=Directory Administrators,$SUFFIX";)
|
||||
aci: (targetattr="userPassword || krbPrincipalKey ||sambaLMPassword || sambaNTPassword")(version 3.0; acl "Kpasswd access to passowrd hashes for passowrd changes"; allow (all) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";)
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
--- /etc/init.d/fedora-ds.orig 2007-07-06 18:21:30.000000000 -0400
|
||||
+++ /etc/init.d/fedora-ds 2007-05-18 19:36:24.000000000 -0400
|
||||
@@ -10,6 +10,9 @@
|
||||
# datadir: /var/lib/fedora-ds/slapd-<instance name>
|
||||
#
|
||||
|
||||
+# Get config.
|
||||
+[ -r /etc/sysconfig/fedora-ds ] && . /etc/sysconfig/fedora-ds
|
||||
+
|
||||
# Source function library.
|
||||
if [ -f /etc/rc.d/init.d/functions ] ; then
|
||||
. /etc/rc.d/init.d/functions
|
||||
@@ -10,6 +10,13 @@
|
||||
ticket_lifetime = 24h
|
||||
forwardable = yes
|
||||
|
||||
[realms]
|
||||
$REALM = {
|
||||
kdc = $FQDN:88
|
||||
admin_server = $FQDN:749
|
||||
default_domain = $DOMAIN
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
.$DOMAIN = $REALM
|
||||
$DOMAIN = $REALM
|
||||
@@ -29,7 +36,7 @@
|
||||
ldap_servers = ldap://127.0.0.1/
|
||||
ldap_kerberos_container_dn = cn=kerberos,$SUFFIX
|
||||
ldap_kdc_dn = uid=kdc,cn=kerberos,$SUFFIX
|
||||
; ldap_kadmind_dn = cn=Directory Manager
|
||||
ldap_kadmind_dn = uid=kdc,cn=kerberos,$SUFFIX
|
||||
ldap_service_password_file = /var/kerberos/krb5kdc/ldappwd
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
VERSION = "%prog .1"
|
||||
|
||||
import socket
|
||||
import logging
|
||||
from optparse import OptionParser
|
||||
import ipa.dsinstance
|
||||
@@ -37,8 +38,6 @@ def parse_options():
|
||||
help="ds user")
|
||||
parser.add_option("-r", "--realm", dest="realm_name",
|
||||
help="realm name")
|
||||
parser.add_option("-a", "--host-address", dest="host_name",
|
||||
help="host address (name or IP address)")
|
||||
parser.add_option("-p", "--password", dest="password",
|
||||
help="admin password")
|
||||
parser.add_option("-m", "--master-password", dest="master_password",
|
||||
@@ -46,8 +45,8 @@ def parse_options():
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if not options.realm_name or not options.host_name or not options.password:
|
||||
parser.error("error: password, realm, and host name required")
|
||||
if not options.ds_user or not options.realm_name or not options.password or not options.master_password:
|
||||
parser.error("error: all options are required")
|
||||
|
||||
return options
|
||||
|
||||
@@ -56,13 +55,35 @@ def main():
|
||||
format='%(asctime)s %(levelname)s %(message)s',
|
||||
filename='ipa-install.log',
|
||||
filemode='w')
|
||||
|
||||
options = parse_options()
|
||||
|
||||
# check the hostname is correctly configured, it must be as the kldap
|
||||
# utilities just use the hostname as returned by gethostbyname to set
|
||||
# up some of the standard entries
|
||||
|
||||
host_name = socket.gethostname()
|
||||
if len(host_name.split(".")) < 2:
|
||||
print "Invalid hostname <"+host_name+">"
|
||||
print "Check the /etc/hosts file and make sure to have a valid FQDN"
|
||||
return "-Fatal Error-"
|
||||
|
||||
if socket.gethostbyname(host_name) == "127.0.0.1":
|
||||
print "The hostname resolves to the localhost address (127.0.0.1)"
|
||||
print "Please change your /etc/hosts file or your DNS so that the"
|
||||
print "hostname resolves to the ip address of your network interface."
|
||||
print "The KDC service does not listen on 127.0.0.1"
|
||||
return "-Fatal Error-"
|
||||
|
||||
print "The Final KDC Host Name will be: " + host_name
|
||||
|
||||
ds = ipa.dsinstance.DsInstance()
|
||||
ds.create_instance(options.ds_user, options.realm_name, options.host_name, options.password)
|
||||
ds.create_instance(options.ds_user, options.realm_name, host_name, options.password)
|
||||
|
||||
krb = ipa.krbinstance.KrbInstance()
|
||||
krb.create_instance(options.ds_user, options.realm_name, options.host_name, options.password, options.master_password)
|
||||
#restart ds after the krb instance have add the sasl map
|
||||
krb.create_instance(options.ds_user, options.realm_name, host_name, options.password, options.master_password)
|
||||
|
||||
#restart ds after the krb instance have add the sasl map and the ldap keytab
|
||||
ds.restart()
|
||||
|
||||
return 0
|
||||
|
||||
@@ -25,6 +25,9 @@ import shutil
|
||||
import logging
|
||||
from random import Random
|
||||
from time import gmtime
|
||||
import os
|
||||
import pwd
|
||||
import socket
|
||||
|
||||
SHARE_DIR = "/usr/share/ipa/"
|
||||
|
||||
@@ -33,6 +36,10 @@ def realm_to_suffix(realm_name):
|
||||
terms = ["dc=" + x.lower() for x in s]
|
||||
return ",".join(terms)
|
||||
|
||||
def host_to_domain(fqdn):
|
||||
s = fqdn.split(".")
|
||||
return ".".join(s[1:])
|
||||
|
||||
def generate_kdc_password():
|
||||
rndpwd = ''
|
||||
r = Random()
|
||||
@@ -75,8 +82,10 @@ def run(args, stdin=None):
|
||||
class KrbInstance:
|
||||
def __init__(self):
|
||||
self.ds_user = None
|
||||
self.realm_name = None
|
||||
self.host_name = None
|
||||
self.fqdn = None
|
||||
self.realm = None
|
||||
self.domain = None
|
||||
self.host = None
|
||||
self.admin_password = None
|
||||
self.master_password = None
|
||||
self.suffix = None
|
||||
@@ -85,12 +94,15 @@ class KrbInstance:
|
||||
|
||||
def create_instance(self, ds_user, realm_name, host_name, admin_password, master_password):
|
||||
self.ds_user = ds_user
|
||||
self.realm_name = realm_name.upper()
|
||||
self.host_name = host_name
|
||||
self.fqdn = host_name
|
||||
self.ip = socket.gethostbyname(host_name)
|
||||
self.realm = realm_name.upper()
|
||||
self.host = host_name.split(".")[0]
|
||||
self.domain = host_to_domain(host_name)
|
||||
self.admin_password = admin_password
|
||||
self.master_password = master_password
|
||||
|
||||
self.suffix = realm_to_suffix(self.realm_name)
|
||||
self.suffix = realm_to_suffix(self.realm)
|
||||
self.kdc_password = generate_kdc_password()
|
||||
self.__configure_kdc_account_password()
|
||||
|
||||
@@ -100,6 +112,10 @@ class KrbInstance:
|
||||
|
||||
self.__create_instance()
|
||||
|
||||
self.__create_ds_keytab()
|
||||
|
||||
self.__create_sample_bind_zone()
|
||||
|
||||
self.start()
|
||||
|
||||
def stop(self):
|
||||
@@ -120,12 +136,13 @@ class KrbInstance:
|
||||
pwd_fd.close()
|
||||
|
||||
def __setup_sub_dict(self):
|
||||
#FIXME: can DOMAIN be different than REALM ?
|
||||
self.sub_dict = dict(FQHN=self.host_name,
|
||||
self.sub_dict = dict(FQDN=self.fqdn,
|
||||
IP=self.ip,
|
||||
PASSWORD=self.kdc_password,
|
||||
SUFFIX=self.suffix,
|
||||
DOMAIN= self.realm_name.lower(),
|
||||
REALM=self.realm_name)
|
||||
DOMAIN=self.domain,
|
||||
HOST=self.host,
|
||||
REALM=self.realm)
|
||||
|
||||
def __configure_ldap(self):
|
||||
|
||||
@@ -153,7 +170,7 @@ class KrbInstance:
|
||||
krb5_fd.close()
|
||||
|
||||
#populate the directory with the realm structure
|
||||
args = ["/usr/kerberos/sbin/kdb5_ldap_util", "-D", "uid=kdc,cn=kerberos,"+self.suffix, "-w", self.kdc_password, "create", "-s", "-r", self.realm_name, "-subtrees", self.suffix, "-sscope", "sub"]
|
||||
args = ["/usr/kerberos/sbin/kdb5_ldap_util", "-D", "uid=kdc,cn=kerberos,"+self.suffix, "-w", self.kdc_password, "create", "-s", "-P", self.master_password, "-r", self.realm, "-subtrees", self.suffix, "-sscope", "sub"]
|
||||
run(args)
|
||||
|
||||
# TODO: NOT called yet, need to find out how to make sure the plugin is available first
|
||||
@@ -165,5 +182,28 @@ class KrbInstance:
|
||||
extop_fd.close()
|
||||
|
||||
#add an ACL to let the DS user read the master key
|
||||
args = ["/usr/bin/setfacl", "-m", "u:"+self.ds_user+":r", "/var/kerberos/krb5kdc/.k5."+self.realm_name]
|
||||
args = ["/usr/bin/setfacl", "-m", "u:"+self.ds_user+":r", "/var/kerberos/krb5kdc/.k5."+self.realm]
|
||||
run(args)
|
||||
|
||||
def __create_sample_bind_zone(self):
|
||||
bind_txt = template_file(SHARE_DIR + "bind.zone.db.template", self.sub_dict)
|
||||
[bind_fd, bind_name] = tempfile.mkstemp(".db","sammple.zone.")
|
||||
os.write(bind_fd, bind_txt)
|
||||
os.close(bind_fd)
|
||||
print "Sample zone file for bind has been created in "+bind_name
|
||||
|
||||
def __create_ds_keytab(self):
|
||||
(kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local")
|
||||
kwrite.write("addprinc -randkey ldap/"+self.fqdn+"@"+self.realm+"\n")
|
||||
kwrite.flush()
|
||||
kwrite.write("ktadd -k /etc/fedora-ds/ds.keytab ldap/"+self.fqdn+"@"+self.realm+"\n")
|
||||
kwrite.flush()
|
||||
kwrite.close()
|
||||
kread.close()
|
||||
kerr.close()
|
||||
|
||||
cfg_fd = open("/etc/sysconfig/fedora-ds", "a")
|
||||
cfg_fd.write("export KRB5_KTNAME=/etc/fedora-ds/ds.keytab\n")
|
||||
cfg_fd.close()
|
||||
pent = pwd.getpwnam(self.ds_user)
|
||||
os.chown("/etc/sysconfig/fedora-ds", pent.pw_uid, pent.pw_gid)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
all:
|
||||
gcc ipa_kpasswd.c -I/usr/include -lkrb5 -llber -lldap -g -o ipa_kpasswd
|
||||
|
||||
install:
|
||||
cp -f ipa_kpasswd /usr/local/sbin/
|
||||
@@ -0,0 +1,2 @@
|
||||
This is an implementation of the RFC3244 kpasswd protocol.
|
||||
It is used to proxy password change operations to Directory Server.
|
||||
@@ -0,0 +1,629 @@
|
||||
|
||||
/* Kpasswd-LDAP proxy */
|
||||
|
||||
/* (C) Simo Sorce */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <krb5.h>
|
||||
#include <ldap.h>
|
||||
#include <sasl/sasl.h>
|
||||
|
||||
#define KPASSWD_PORT 464
|
||||
#define KPASSWD_TCP 1
|
||||
#define KPASSWD_UDP 2
|
||||
|
||||
int debug = 1;
|
||||
char *srv_pri_name = "kadmin/changepw";
|
||||
char *keytab_name = "FILE:/var/kerberos/krb5kdc/kpasswd.keytab";
|
||||
char *realm_name = "BLUEBOX.REDHAT.COM";
|
||||
char *ldap_uri = "ldap://rc1.bluebox.redhat.com:389";
|
||||
|
||||
int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *sit)
|
||||
{
|
||||
sasl_interact_t **in = sit;
|
||||
int i, ret = LDAP_OTHER;
|
||||
|
||||
if (!ld) return LDAP_PARAM_ERROR;
|
||||
|
||||
for (i = 0; in[i] && in[i]->id != SASL_CB_LIST_END; i++) {
|
||||
switch(in[i]->id) {
|
||||
case SASL_CB_USER:
|
||||
in[i]->result = srv_pri_name;
|
||||
in[i]->len = strlen(srv_pri_name);
|
||||
ret = LDAP_SUCCESS;
|
||||
break;
|
||||
case SASL_CB_GETREALM:
|
||||
in[i]->result = realm_name;
|
||||
in[i]->len = strlen(realm_name);
|
||||
ret = LDAP_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
if (debug > 0) {
|
||||
fprintf(stderr,
|
||||
"Unhandled SASL int. option %d\n",
|
||||
in[i]->id);
|
||||
}
|
||||
in[i]->result = NULL;
|
||||
in[i]->len = 0;
|
||||
ret = LDAP_OTHER;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ldap_pwd_change(char *client_name, krb5_data pwd)
|
||||
{
|
||||
int id, version;
|
||||
LDAP *ld = NULL;
|
||||
BerElement *ctrl = NULL;
|
||||
struct berval control;
|
||||
struct berval newpw;
|
||||
char *userdn = NULL;
|
||||
int ret;
|
||||
|
||||
newpw.bv_len = pwd.length;
|
||||
newpw.bv_val = pwd.data;
|
||||
|
||||
/* connect to ldap server */
|
||||
/* TODO: support referrals ? */
|
||||
ret = ldap_initialize(&ld, ldap_uri);
|
||||
if(ret != LDAP_SUCCESS) {
|
||||
fprintf(stderr, "Unable to connect to ldap server");
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
version = LDAP_VERSION3;
|
||||
ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
|
||||
if (ret != LDAP_OPT_SUCCESS) {
|
||||
fprintf(stderr, "Unable to set ldap protocol version");
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = ldap_sasl_interactive_bind_s(ld,
|
||||
NULL, "GSSAPI",
|
||||
NULL, NULL,
|
||||
LDAP_SASL_AUTOMATIC,
|
||||
ldap_sasl_interact, NULL);
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
fprintf(stderr, "Unable to bind to ldap server");
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* find user dn */
|
||||
|
||||
/* build password change control */
|
||||
ctrl = ber_alloc_t(LBER_USE_DER);
|
||||
if (!ctrl) {
|
||||
fprintf(stderr, "Out of memory!\n");
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
ber_printf(ctrl, "{tstON}",
|
||||
LDAP_TAG_EXOP_MODIFY_PASSWD_ID, userdn,
|
||||
LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &newpw);
|
||||
|
||||
ret = ber_flatten2(ctrl, &control, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ber flattening failed!\n");
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* perform poassword change */
|
||||
ret = ldap_extended_operation(ld, LDAP_EXOP_MODIFY_PASSWD,
|
||||
&control, NULL, NULL, &id);
|
||||
done:
|
||||
if (ctrl) ber_free(ctrl, 1);
|
||||
if (ld)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void handle_krb_packets(uint8_t *buf, ssize_t buflen,
|
||||
struct sockaddr_in *from,
|
||||
uint8_t **repbuf, ssize_t *replen)
|
||||
{
|
||||
krb5_auth_context auth_context;
|
||||
krb5_context context;
|
||||
krb5_keytab keytab;
|
||||
krb5_principal kprincpw;
|
||||
krb5_ticket *ticket;
|
||||
krb5_address **lkaddr, rkaddr;
|
||||
krb5_data kreq, krep, kenc, kdec;
|
||||
krb5_replay_data replay;
|
||||
krb5_error krb5err;
|
||||
int krberr, err;
|
||||
struct sockaddr_in laddr, raddr;
|
||||
socklen_t addrlen;
|
||||
size_t reqlen;
|
||||
size_t verno;
|
||||
char *client_name;
|
||||
char *result_string;
|
||||
int result_err;
|
||||
uint8_t *reply;
|
||||
ssize_t replylen;
|
||||
|
||||
*replen = 0;
|
||||
|
||||
auth_context = NULL;
|
||||
krep.length = 0;
|
||||
krep.data = NULL;
|
||||
kprincpw = NULL;
|
||||
ticket = NULL;
|
||||
lkaddr = NULL;
|
||||
|
||||
rkaddr.addrtype = ADDRTYPE_INET;
|
||||
rkaddr.length = sizeof(from->sin_addr);
|
||||
rkaddr.contents = (krb5_octet *) &from->sin_addr;
|
||||
|
||||
if (buflen < 4) {
|
||||
result_string = "Request truncated";
|
||||
result_err = KRB5_KPASSWD_MALFORMED;
|
||||
fprintf(stderr, "%s\n", result_string);
|
||||
goto done;
|
||||
}
|
||||
|
||||
reqlen = (buf[0] << 8) + buf[1];
|
||||
|
||||
if (reqlen != buflen) {
|
||||
result_string = "Unmatching request length";
|
||||
result_err = KRB5_KPASSWD_MALFORMED;
|
||||
fprintf(stderr, "%s\n", result_string);
|
||||
goto done;
|
||||
}
|
||||
|
||||
verno = (buf[2] << 8) + buf[3];
|
||||
|
||||
if (verno != 1) {
|
||||
result_string = "Unsupported version";
|
||||
result_err = KRB5_KPASSWD_BAD_VERSION;
|
||||
fprintf(stderr, "%s\n", result_string);
|
||||
goto done;
|
||||
}
|
||||
|
||||
kreq.length = (buf[4] << 8) + buf[5];
|
||||
if (kreq.length > (buflen - 6)) {
|
||||
result_string = "Request truncated";
|
||||
result_err = KRB5_KPASSWD_MALFORMED;
|
||||
fprintf(stderr, "%s\n", result_string);
|
||||
goto done;
|
||||
}
|
||||
kreq.data = &buf[6];
|
||||
|
||||
krberr = krb5_init_context(&context);
|
||||
if (krberr) {
|
||||
result_string = "Failed to init kerberos context";
|
||||
result_err = KRB5_KPASSWD_HARDERROR;
|
||||
fprintf(stderr, "%s\n", result_string);
|
||||
goto done;
|
||||
}
|
||||
|
||||
krberr = krb5_auth_con_init(context, &auth_context);
|
||||
if (krberr) {
|
||||
result_string = "Unable to init auth context";
|
||||
result_err = KRB5_KPASSWD_HARDERROR;
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
|
||||
krberr = krb5_auth_con_setflags(context, auth_context,
|
||||
KRB5_AUTH_CONTEXT_DO_SEQUENCE);
|
||||
if (krberr) {
|
||||
result_string = "Unable to init auth context";
|
||||
result_err = KRB5_KPASSWD_HARDERROR;
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
|
||||
krberr = krb5_build_principal(context, &kprincpw,
|
||||
strlen(realm_name), realm_name,
|
||||
"kadmin", "changepw", NULL);
|
||||
if (krberr) {
|
||||
result_string = "Unable to build principal";
|
||||
result_err = KRB5_KPASSWD_HARDERROR;
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
|
||||
krberr = krb5_kt_resolve(context, keytab_name, &keytab);
|
||||
if (krberr) {
|
||||
result_string = "Unable to retrieve keytab";
|
||||
result_err = KRB5_KPASSWD_HARDERROR;
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
|
||||
krberr = krb5_rd_req(context, &auth_context, &kreq,
|
||||
kprincpw, keytab, NULL, &ticket);
|
||||
if (krberr) {
|
||||
result_string = "Unable to read request";
|
||||
result_err = KRB5_KPASSWD_AUTHERROR;
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* build the AP Reply before actually changing the password
|
||||
* this minimize the risk of a fatal error occurring _after_
|
||||
* the password have been successfully changed */
|
||||
krberr = krb5_mk_rep(context, auth_context, &krep);
|
||||
if (krberr) {
|
||||
result_string = "Failed to to build reply";
|
||||
result_err = KRB5_KPASSWD_HARDERROR;
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* verify that this is an AS_REQ ticket */
|
||||
if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) {
|
||||
result_string = "Ticket must be derived from a password";
|
||||
result_err = KRB5_KPASSWD_AUTHERROR;
|
||||
fprintf(stderr, "%s\n", result_string);
|
||||
goto kpreply;
|
||||
}
|
||||
|
||||
krberr = krb5_unparse_name(context, ticket->enc_part2->client,
|
||||
&client_name);
|
||||
if (krberr) {
|
||||
result_string = "Unable to parse client name";
|
||||
result_err = KRB5_KPASSWD_HARDERROR;
|
||||
fprintf(stderr, "%s\n", result_string);
|
||||
goto kpreply;
|
||||
}
|
||||
|
||||
krberr = krb5_auth_con_setaddrs(context, auth_context, NULL, &rkaddr);
|
||||
if (krberr) {
|
||||
result_string = "Failed to set client address";
|
||||
result_err = KRB5_KPASSWD_HARDERROR;
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto kpreply;
|
||||
}
|
||||
|
||||
/* decrypt the new password */
|
||||
kenc.length = reqlen - kreq.length - 6;
|
||||
kenc.data = kreq.data + kreq.length;
|
||||
|
||||
/* rd_priv needs the remote address while mk_priv (used later)
|
||||
* requires the local address (from kadmin code) */
|
||||
krberr = krb5_rd_priv(context, auth_context, &kenc, &kdec, &replay);
|
||||
if (krberr) {
|
||||
result_string = "Failed to decrypt password";
|
||||
result_err = KRB5_KPASSWD_HARDERROR;
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto kpreply;
|
||||
}
|
||||
|
||||
if (debug > 0) {
|
||||
fprintf(stderr, "Client %s trying to set password [%*s]\n",
|
||||
client_name, kdec.length, kdec.data);
|
||||
}
|
||||
|
||||
err = ldap_pwd_change(client_name, kdec);
|
||||
|
||||
/* ok we are done, and the password change was successful! */
|
||||
result_err = KRB5_KPASSWD_SUCCESS;
|
||||
result_string = "";
|
||||
|
||||
/* make sure password is cleared off before we free the memory */
|
||||
memset(kdec.data, 0, kdec.length);
|
||||
free(kdec.data);
|
||||
|
||||
kpreply:
|
||||
|
||||
/* set-up the the clear text reply */
|
||||
kdec.length = 2 + strlen(result_string);
|
||||
kdec.data = malloc(kdec.length);
|
||||
if (!kdec.data) {
|
||||
fprintf(stderr, "Out of memory!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
kdec.data[0] = (result_err >> 8) & 0xff;
|
||||
kdec.data[1] = result_err & 0xff;
|
||||
memcpy(&kdec.data[2], result_string, strlen(result_string));
|
||||
|
||||
/* we listen on ANYADDR, use this retrieve the right address */
|
||||
krberr = krb5_os_localaddr(context, &lkaddr);
|
||||
if (krberr) {
|
||||
result_string = "Failed to retrieve local address";
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
|
||||
krberr = krb5_auth_con_setaddrs(context, auth_context, lkaddr[0], NULL);
|
||||
if (krberr) {
|
||||
result_string = "Failed to set local address";
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
|
||||
krberr = krb5_mk_priv(context, auth_context, &kdec, &kenc, &replay);
|
||||
if (krberr) {
|
||||
result_string = "Failed to encrypt reply message";
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
/* encryption was unsuccessful, let's return a krb error */
|
||||
|
||||
/* the ap data is no more useful */
|
||||
free(krep.data);
|
||||
krep.length = 0;
|
||||
|
||||
/* build a krberror encrypted paylod */
|
||||
krb5err.error = KRB5_CHPW_FAIL;
|
||||
krb5err.server = kprincpw;
|
||||
krb5err.client = NULL;
|
||||
krb5err.ctime = 0;
|
||||
krb5err.cusec = 0;
|
||||
krb5err.susec = 0;
|
||||
krberr = krb5_timeofday(context, &krb5err.stime);
|
||||
if (krberr) {
|
||||
result_string = "Failed to set time of day";
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
|
||||
krb5err.text.length = 0;
|
||||
krb5err.e_data = kdec;
|
||||
krberr = krb5_mk_error(context, &krb5err, &kenc);
|
||||
if (krberr) {
|
||||
result_string = "Failed to build error message";
|
||||
fprintf(stderr, "%s: %s\n", result_string,
|
||||
krb5_get_error_message(context, krberr));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
replylen = 6 + krep.length + kenc.length;
|
||||
reply = malloc(replylen);
|
||||
if (!reply) {
|
||||
fprintf(stderr, "Out of memory!\n");
|
||||
goto done;
|
||||
}
|
||||
*repbuf = reply;
|
||||
|
||||
reply[0] = (replylen >> 8) & 0xff;
|
||||
reply[1] = replylen & 0xff;
|
||||
reply[2] = 0x00;
|
||||
reply[3] = 0x01;
|
||||
reply[4] = (krep.length >> 8) & 0xff;
|
||||
reply[5] = krep.length & 0xff;
|
||||
|
||||
if (krep.length) {
|
||||
memcpy(&reply[6], krep.data, krep.length);
|
||||
}
|
||||
memcpy(&reply[6 + krep.length], kenc.data, kenc.length);
|
||||
|
||||
*replen = replylen;
|
||||
|
||||
done:
|
||||
if (auth_context) krb5_auth_con_free(context, auth_context);
|
||||
if (kprincpw) krb5_free_principal(context, kprincpw);
|
||||
if (krep.length) free(krep.data);
|
||||
if (ticket) krb5_free_ticket(context, ticket);
|
||||
if (kdec.length) free(kdec.data);
|
||||
if (lkaddr) krb5_free_addresses(context, lkaddr);
|
||||
krb5_free_context(context);
|
||||
}
|
||||
|
||||
pid_t handle_conn(int fd, int type)
|
||||
{
|
||||
int mfd;
|
||||
pid_t pid;
|
||||
uint8_t request[1500];
|
||||
ssize_t reqlen;
|
||||
uint8_t *reply;
|
||||
ssize_t replen;
|
||||
struct sockaddr_in from;
|
||||
socklen_t fromlen;
|
||||
|
||||
fromlen = sizeof(from);
|
||||
|
||||
/* receive request */
|
||||
if (type == KPASSWD_TCP) {
|
||||
|
||||
mfd = accept(fd, (struct sockaddr *)&from, &fromlen);
|
||||
if (mfd == -1) {
|
||||
fprintf(stderr, "Accept failed with error (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
mfd = fd;
|
||||
}
|
||||
|
||||
reqlen = recvfrom(mfd, request, sizeof(request), 0,
|
||||
(struct sockaddr *)&from, &fromlen);
|
||||
if (reqlen <= 0) {
|
||||
fprintf(stderr, "Error receiving request (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
if (type == KPASSWD_TCP) close(mfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (debug > 0) {
|
||||
uint32_t host = ntohl(from.sin_addr.s_addr);
|
||||
uint16_t port = ntohs(from.sin_port);
|
||||
fprintf(stderr,
|
||||
"Connection from %d.%d.%d.%d:%d\n",
|
||||
(host & 0xff000000) >> 24,
|
||||
(host & 0x00ff0000) >> 16,
|
||||
(host & 0x0000ff00) >> 8,
|
||||
host & 0x000000ff,
|
||||
port);
|
||||
}
|
||||
|
||||
#if 1
|
||||
/* handle kerberos and ldap operations in childrens */
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
fprintf(stderr, "Fork failed with error (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
if (type == KPASSWD_TCP) close(mfd);
|
||||
return 0;
|
||||
}
|
||||
if (pid != 0) { /* parent */
|
||||
if (type == KPASSWD_TCP) close(mfd);
|
||||
return pid;
|
||||
}
|
||||
#endif
|
||||
/* children */
|
||||
handle_krb_packets(request, reqlen, &from, &reply, &replen);
|
||||
|
||||
if (replen) { /* we have something to reply */
|
||||
if (type == KPASSWD_TCP) {
|
||||
sendto(mfd, reply, replen, 0, NULL, 0);
|
||||
} else {
|
||||
sendto(mfd, reply, replen, 0, (struct sockaddr *)&from, fromlen);
|
||||
}
|
||||
}
|
||||
close(mfd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int tcp_s, udp_s;
|
||||
int tru = 1;
|
||||
int ret;
|
||||
|
||||
tcp_s = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (tcp_s == -1) {
|
||||
fprintf(stderr, "Unable to create TCP socket\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
udp_s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (udp_s == -1) {
|
||||
fprintf(stderr, "Unable to create UDP socket\n");
|
||||
close(tcp_s);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* make sockets immediately reusable */
|
||||
ret = setsockopt(tcp_s, SOL_SOCKET, SO_REUSEADDR,
|
||||
(void *)&tru, sizeof(tru));
|
||||
if (ret == -1) {
|
||||
fprintf(stderr,
|
||||
"Unable to set SO_REUSEADDR for the TCP socket (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
close(tcp_s);
|
||||
close(udp_s);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
ret = setsockopt(udp_s, SOL_SOCKET, SO_REUSEADDR,
|
||||
(void *)&tru, sizeof(tru));
|
||||
if (ret == -1) {
|
||||
fprintf(stderr,
|
||||
"Unable to set SO_REUSEADDR for the UDP socket (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
close(tcp_s);
|
||||
close(udp_s);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
/* bind sockets */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(KPASSWD_PORT);
|
||||
|
||||
ret = bind(tcp_s, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (ret == -1) {
|
||||
fprintf(stderr,
|
||||
"Unable to bind the TCP kpasswd port (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
close(tcp_s);
|
||||
close(udp_s);
|
||||
exit(3);
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(KPASSWD_PORT);
|
||||
|
||||
ret = bind(udp_s, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (ret == -1) {
|
||||
fprintf(stderr,
|
||||
"Unable to bind the UDP kpasswd port (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
close(tcp_s);
|
||||
close(udp_s);
|
||||
exit(3);
|
||||
}
|
||||
|
||||
ret = listen(tcp_s, 5);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr,
|
||||
"Unable to listen oin the TCP socket (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
close(tcp_s);
|
||||
close(udp_s);
|
||||
exit(4);
|
||||
}
|
||||
|
||||
/* now that sockets are set up, enter the select loop */
|
||||
|
||||
while (1) {
|
||||
int cstatus;
|
||||
fd_set rfd;
|
||||
|
||||
FD_ZERO(&rfd);
|
||||
FD_SET(udp_s, &rfd);
|
||||
FD_SET(tcp_s, &rfd);
|
||||
|
||||
ret = select(udp_s+1, &rfd, NULL, NULL, NULL);
|
||||
|
||||
switch(ret) {
|
||||
case 0:
|
||||
break;
|
||||
case -1:
|
||||
if (errno != EINTR) {
|
||||
fprintf(stderr,
|
||||
"Unexpected error in select (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
exit(5);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (FD_ISSET(tcp_s, &rfd)) {
|
||||
handle_conn(tcp_s, KPASSWD_TCP);
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET(udp_s, &rfd)) {
|
||||
handle_conn(udp_s, KPASSWD_UDP);
|
||||
break;
|
||||
}
|
||||
/* what else?? */
|
||||
fprintf(stderr, "Select returned but no fd ready\n");
|
||||
exit(6);
|
||||
}
|
||||
|
||||
/* check for children exiting */
|
||||
waitpid(-1, &cstatus, WNOHANG);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user