1/* $NetBSD: hpropd.c,v 1.3 2023/06/19 21:41:41 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997-2006 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "hprop.h" 37 38static int inetd_flag = -1; 39static int help_flag; 40static int version_flag; 41static int print_dump; 42static const char *database; 43static int from_stdin; 44static char *local_realm; 45static char *ktname = NULL; 46 47struct getargs args[] = { 48 { "database", 'd', arg_string, rk_UNCONST(&database), "database", "file" }, 49 { "stdin", 'n', arg_flag, &from_stdin, "read from stdin", NULL }, 50 { "print", 0, arg_flag, &print_dump, "print dump to stdout", NULL }, 51#ifdef SUPPORT_INETD 52 { "inetd", 'i', arg_negative_flag, &inetd_flag, 53 "Not started from inetd", NULL }, 54#endif 55 { "keytab", 'k', arg_string, &ktname, "keytab to use for authentication", "keytab" }, 56 { "realm", 'r', arg_string, &local_realm, "realm to use", NULL }, 57 { "version", 0, arg_flag, &version_flag, NULL, NULL }, 58 { "help", 'h', arg_flag, &help_flag, NULL, NULL} 59}; 60 61static int num_args = sizeof(args) / sizeof(args[0]); 62static char unparseable_name[] = "unparseable name"; 63 64static void 65usage(int ret) 66{ 67 arg_printusage (args, num_args, NULL, ""); 68 exit (ret); 69} 70 71int 72main(int argc, char **argv) 73{ 74 krb5_error_code ret; 75 krb5_context context; 76 krb5_auth_context ac = NULL; 77 krb5_principal c1, c2; 78 krb5_authenticator authent; 79 krb5_keytab keytab; 80 krb5_socket_t sock = rk_INVALID_SOCKET; 81 HDB *db = NULL; 82 int optidx = 0; 83 char *tmp_db; 84 krb5_log_facility *fac; 85 int nprincs; 86 87 setprogname(argv[0]); 88 89 ret = krb5_init_context(&context); 90 if (ret) 91 exit(1); 92 93 ret = krb5_openlog(context, "hpropd", &fac); 94 if (ret) 95 errx(1, "krb5_openlog"); 96 krb5_set_warn_dest(context, fac); 97 98 if (getarg(args, num_args, argc, argv, &optidx)) 99 usage(1); 100 101 if (local_realm != NULL) 102 krb5_set_default_realm(context, local_realm); 103 104 if (help_flag) 105 usage(0); 106 if (version_flag) { 107 print_version(NULL); 108 exit(0); 109 } 110 111 argc -= optidx; 112#ifndef __clang_analyzer__ 113 argv += optidx; 114#endif 115 116 if (argc != 0) 117 usage(1); 118 119 if (database == NULL) 120 database = hdb_default_db(context); 121 122 if (from_stdin) { 123 sock = STDIN_FILENO; 124 } else { 125 struct sockaddr_storage ss; 126 struct sockaddr *sa = (struct sockaddr *)&ss; 127 socklen_t sin_len = sizeof(ss); 128 char addr_name[256]; 129 krb5_ticket *ticket; 130 char *server; 131 132 memset(&ss, 0, sizeof(ss)); 133 sock = STDIN_FILENO; 134#ifdef SUPPORT_INETD 135 if (inetd_flag == -1) { 136 if (getpeername (sock, sa, &sin_len) < 0) { 137 inetd_flag = 0; 138 } else { 139 inetd_flag = 1; 140 } 141 } 142#else 143 inetd_flag = 0; 144#endif 145 if (!inetd_flag) { 146 mini_inetd (krb5_getportbyname (context, "hprop", "tcp", 147 HPROP_PORT), &sock); 148 } 149 sin_len = sizeof(ss); 150 if (getpeername(sock, sa, &sin_len) < 0) 151 krb5_err(context, 1, errno, "getpeername"); 152 153 if (inet_ntop(sa->sa_family, 154 socket_get_address (sa), 155 addr_name, 156 sizeof(addr_name)) == NULL) 157 strlcpy (addr_name, "unknown address", 158 sizeof(addr_name)); 159 160 krb5_log(context, fac, 0, "Connection from %s", addr_name); 161 162 ret = krb5_kt_register(context, &hdb_get_kt_ops); 163 if (ret) 164 krb5_err(context, 1, ret, "krb5_kt_register"); 165 166 if (ktname != NULL) { 167 ret = krb5_kt_resolve(context, ktname, &keytab); 168 if (ret) 169 krb5_err (context, 1, ret, "krb5_kt_resolve %s", ktname); 170 } else { 171 ret = krb5_kt_default (context, &keytab); 172 if (ret) 173 krb5_err (context, 1, ret, "krb5_kt_default"); 174 } 175 176 ret = krb5_recvauth(context, &ac, &sock, HPROP_VERSION, NULL, 177 0, keytab, &ticket); 178 if (ret) 179 krb5_err(context, 1, ret, "krb5_recvauth"); 180 181 ret = krb5_unparse_name(context, ticket->server, &server); 182 if (ret) 183 krb5_err(context, 1, ret, "krb5_unparse_name"); 184 if (strncmp(server, "hprop/", 5) != 0) 185 krb5_errx(context, 1, "ticket not for hprop (%s)", server); 186 187 free(server); 188 krb5_free_ticket (context, ticket); 189 190 ret = krb5_auth_con_getauthenticator(context, ac, &authent); 191 if (ret) 192 krb5_err(context, 1, ret, "krb5_auth_con_getauthenticator"); 193 194 ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL); 195 if (ret) 196 krb5_err(context, 1, ret, "krb5_make_principal"); 197 _krb5_principalname2krb5_principal(context, &c2, 198 authent->cname, authent->crealm); 199 if (!krb5_principal_compare(context, c1, c2)) { 200 char *s; 201 ret = krb5_unparse_name(context, c2, &s); 202 if (ret) 203 s = unparseable_name; 204 krb5_errx(context, 1, "Unauthorized connection from %s", s); 205 } 206 krb5_free_principal(context, c1); 207 krb5_free_principal(context, c2); 208 209 ret = krb5_kt_close(context, keytab); 210 if (ret) 211 krb5_err(context, 1, ret, "krb5_kt_close"); 212 } 213 214 if (!print_dump) { 215 int aret; 216 217 aret = asprintf(&tmp_db, "%s~", database); 218 if (aret == -1) 219 krb5_errx(context, 1, "hdb_create: out of memory"); 220 221 ret = hdb_create(context, &db, tmp_db); 222 if (ret) 223 krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db); 224 ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600); 225 if (ret) 226 krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db); 227 } 228 229 nprincs = 0; 230 while (1){ 231 krb5_data data; 232 hdb_entry_ex entry; 233 234 if (from_stdin) { 235 ret = krb5_read_message(context, &sock, &data); 236 if (ret != 0 && ret != HEIM_ERR_EOF) 237 krb5_err(context, 1, ret, "krb5_read_message"); 238 } else { 239 ret = krb5_read_priv_message(context, ac, &sock, &data); 240 if (ret) 241 krb5_err(context, 1, ret, "krb5_read_priv_message"); 242 } 243 244 if (ret == HEIM_ERR_EOF || data.length == 0) { 245 if (!from_stdin) { 246 data.data = NULL; 247 data.length = 0; 248 krb5_write_priv_message(context, ac, &sock, &data); 249 } 250 if (!print_dump) { 251 ret = db->hdb_close(context, db); 252 if (ret) 253 krb5_err(context, 1, ret, "db_close"); 254 ret = db->hdb_rename(context, db, database); 255 if (ret) 256 krb5_err(context, 1, ret, "db_rename"); 257 } 258 break; 259 } 260 memset(&entry, 0, sizeof(entry)); 261 ret = hdb_value2entry(context, &data, &entry.entry); 262 krb5_data_free(&data); 263 if (ret) 264 krb5_err(context, 1, ret, "hdb_value2entry"); 265 if (print_dump) { 266 struct hdb_print_entry_arg parg; 267 268 parg.out = stdout; 269 parg.fmt = HDB_DUMP_HEIMDAL; 270 hdb_print_entry(context, db, &entry, &parg); 271 } else { 272 ret = db->hdb_store(context, db, 0, &entry); 273 if (ret == HDB_ERR_EXISTS) { 274 char *s; 275 ret = krb5_unparse_name(context, entry.entry.principal, &s); 276 if (ret) 277 s = strdup(unparseable_name); 278 krb5_warnx(context, "Entry exists: %s", s); 279 free(s); 280 } else if (ret) 281 krb5_err(context, 1, ret, "db_store"); 282 else 283 nprincs++; 284 } 285 hdb_free_entry(context, &entry); 286 } 287 if (!print_dump) 288 krb5_log(context, fac, 0, "Received %d principals", nprincs); 289 290 if (inetd_flag == 0) 291 rk_closesocket(sock); 292 293 exit(0); 294} 295