summaryrefslogtreecommitdiffstats
path: root/0002-Add-helper-to-determine-if-a-KDC-is-the-master.patch
blob: 50ae55b8c63b72f5a85c3801119b9d0abd939972 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
From f4b1a7e7b80ce68e57912edcd48c39ea62c73e43 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sun, 6 Apr 2014 18:06:14 -0400
Subject: [PATCH 02/13] Add helper to determine if a KDC is the master

Add a new function k5_kdc_is_master in locate_kdc.c to determine
whether a KDC matches one of the masters, and use it in
krb5_sendto_kdc.
---
 src/lib/krb5/os/locate_kdc.c | 110 +++++++++++++++++++++++++++++--------------
 src/lib/krb5/os/os-proto.h   |   3 ++
 src/lib/krb5/os/sendto_kdc.c |  31 +-----------
 3 files changed, 80 insertions(+), 64 deletions(-)

diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c
index 88d55a8..4479465 100644
--- a/src/lib/krb5/os/locate_kdc.c
+++ b/src/lib/krb5/os/locate_kdc.c
@@ -166,6 +166,24 @@ add_host_to_list(struct serverlist *list, const char *hostname, int port,
     return 0;
 }
 
+/* Return true if server is identical to an entry in list. */
+static krb5_boolean
+server_list_contains(struct serverlist *list, struct server_entry *server)
+{
+    struct server_entry *ent;
+
+    for (ent = list->servers; ent < list->servers + list->nservers; ent++) {
+        if (server->hostname != NULL && ent->hostname != NULL &&
+            strcmp(server->hostname, ent->hostname) == 0)
+            return TRUE;
+        if (server->hostname == NULL && ent->hostname == NULL &&
+            server->addrlen == ent->addrlen &&
+            memcmp(&server->addr, &ent->addr, server->addrlen) == 0)
+            return TRUE;
+    }
+    return FALSE;
+}
+
 static krb5_error_code
 locate_srv_conf_1(krb5_context context, const krb5_data *realm,
                   const char * name, struct serverlist *serverlist,
@@ -529,6 +547,41 @@ dns_locate_server(krb5_context context, const krb5_data *realm,
 }
 #endif /* KRB5_DNS_LOOKUP */
 
+static krb5_error_code
+locate_server(krb5_context context, const krb5_data *realm,
+              struct serverlist *serverlist, enum locate_service_type svc,
+              int socktype)
+{
+    krb5_error_code ret;
+    struct serverlist list = SERVERLIST_INIT;
+
+    *serverlist = list;
+
+    /* Try modules.  If a module returns 0 but leaves the list empty, return an
+     * empty list. */
+    ret = module_locate_server(context, realm, &list, svc, socktype);
+    if (ret != KRB5_PLUGIN_NO_HANDLE)
+        goto done;
+
+    /* Try the profile.  Fall back to DNS if it returns an empty list. */
+    ret = prof_locate_server(context, realm, &list, svc, socktype);
+    if (ret)
+        goto done;
+
+#ifdef KRB5_DNS_LOOKUP
+    if (list.nservers == 0)
+        ret = dns_locate_server(context, realm, &list, svc, socktype);
+#endif
+
+done:
+    if (ret) {
+        k5_free_serverlist(&list);
+        return ret;
+    }
+    *serverlist = list;
+    return 0;
+}
+
 /*
  * Wrapper function for the various backends
  */
@@ -538,54 +591,26 @@ k5_locate_server(krb5_context context, const krb5_data *realm,
                  struct serverlist *serverlist, enum locate_service_type svc,
                  int socktype)
 {
-    krb5_error_code code;
-    struct serverlist al = SERVERLIST_INIT;
-
-    *serverlist = al;
+    krb5_error_code ret;
 
+    memset(serverlist, 0, sizeof(*serverlist));
     if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
         krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
                                "Cannot find KDC for invalid realm name \"\"");
         return KRB5_REALM_CANT_RESOLVE;
     }
 
-    code = module_locate_server(context, realm, &al, svc, socktype);
-    Tprintf("module_locate_server returns %d\n", code);
-    if (code == KRB5_PLUGIN_NO_HANDLE) {
-        /*
-         * We always try the local file before DNS.  Note that there
-         * is no way to indicate "service not available" via the
-         * config file.
-         */
-
-        code = prof_locate_server(context, realm, &al, svc, socktype);
-
-#ifdef KRB5_DNS_LOOKUP
-        if (code == 0 && al.nservers == 0)
-            code = dns_locate_server(context, realm, &al, svc, socktype);
-#endif /* KRB5_DNS_LOOKUP */
+    ret = locate_server(context, realm, serverlist, svc, socktype);
+    if (ret)
+        return ret;
 
-        /* We could put more heuristics here, like looking up a hostname
-           of "kerberos."+REALM, etc.  */
-    }
-    if (code == 0)
-        Tprintf ("krb5int_locate_server found %d addresses\n",
-                 al.nservers);
-    else
-        Tprintf ("krb5int_locate_server returning error code %d/%s\n",
-                 code, error_message(code));
-    if (code != 0) {
-        k5_free_serverlist(&al);
-        return code;
-    }
-    if (al.nservers == 0) {       /* No good servers */
-        k5_free_serverlist(&al);
+    if (serverlist->nservers == 0) {
+        k5_free_serverlist(serverlist);
         krb5_set_error_message(context, KRB5_REALM_UNKNOWN,
                                _("Cannot find KDC for realm \"%.*s\""),
                                realm->length, realm->data);
         return KRB5_REALM_UNKNOWN;
     }
-    *serverlist = al;
     return 0;
 }
 
@@ -598,3 +623,18 @@ k5_locate_kdc(krb5_context context, const krb5_data *realm,
     stype = get_masters ? locate_service_master_kdc : locate_service_kdc;
     return k5_locate_server(context, realm, serverlist, stype, socktype);
 }
+
+krb5_boolean
+k5_kdc_is_master(krb5_context context, const krb5_data *realm,
+                 struct server_entry *server)
+{
+    struct serverlist list;
+    krb5_boolean found;
+
+    if (locate_server(context, realm, &list, locate_service_master_kdc,
+                      server->socktype) != 0)
+        return FALSE;
+    found = server_list_contains(&list, server);
+    k5_free_serverlist(&list);
+    return found;
+}
diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
index c6b730f..9125ba0 100644
--- a/src/lib/krb5/os/os-proto.h
+++ b/src/lib/krb5/os/os-proto.h
@@ -76,6 +76,9 @@ krb5_error_code k5_locate_kdc(krb5_context context, const krb5_data *realm,
                               struct serverlist *serverlist, int get_masters,
                               int socktype);
 
+krb5_boolean k5_kdc_is_master(krb5_context context, const krb5_data *realm,
+                              struct server_entry *server);
+
 void k5_free_serverlist(struct serverlist *);
 
 #ifdef HAVE_NETINET_IN_H
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 5f781d3..e3855a3 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -293,25 +293,6 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
 }
 
 static int
-in_addrlist(struct server_entry *entry, struct serverlist *list)
-{
-    size_t i;
-    struct server_entry *le;
-
-    for (i = 0; i < list->nservers; i++) {
-        le = &list->servers[i];
-        if (entry->hostname != NULL && le->hostname != NULL &&
-            strcmp(entry->hostname, le->hostname) == 0)
-            return 1;
-        if (entry->hostname == NULL && le->hostname == NULL &&
-            entry->addrlen == le->addrlen &&
-            memcmp(&entry->addr, &le->addr, entry->addrlen) == 0)
-            return 1;
-    }
-    return 0;
-}
-
-static int
 check_for_svc_unavailable (krb5_context context,
                            const krb5_data *reply,
                            void *msg_handler_data)
@@ -418,17 +399,9 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
     /* Set use_master to 1 if we ended up talking to a master when we didn't
      * explicitly request to. */
     if (*use_master == 0) {
-        struct serverlist mservers;
-        struct server_entry *entry = &servers.servers[server_used];
-        retval = k5_locate_kdc(context, realm, &mservers, TRUE,
-                               entry->socktype);
-        if (retval == 0) {
-            if (in_addrlist(entry, &mservers))
-                *use_master = 1;
-            k5_free_serverlist(&mservers);
-        }
+        *use_master = k5_kdc_is_master(context, realm,
+                                       &servers.servers[server_used]);
         TRACE_SENDTO_KDC_MASTER(context, *use_master);
-        retval = 0;
     }
 
 cleanup:
-- 
2.1.0