1/*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
7 * Reserved.  This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License').  You may not use this file
10 * except in compliance with the License.  Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24/*
25 * Copyright (c) 1983, 1993
26 *	The Regents of the University of California.  All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 *    notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 *    notice, this list of conditions and the following disclaimer in the
35 *    documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 *    must display the following acknowledgement:
38 *	This product includes software developed by the University of
39 *	California, Berkeley and its contributors.
40 * 4. Neither the name of the University nor the names of its contributors
41 *    may be used to endorse or promote products derived from this software
42 *    without specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57#if !defined(__APPLE__) || defined(KERBEROS)
58
59#include <sys/param.h>
60#include <sys/file.h>
61#include <sys/socket.h>
62#include <sys/stat.h>
63
64#include <netinet/in.h>
65#include <arpa/inet.h>
66
67#include <kerberosIV/des.h>
68#include <kerberosIV/krb.h>
69#include <kerberosIV/kparse.h>
70
71#include <ctype.h>
72#include <errno.h>
73#include <netdb.h>
74#include <pwd.h>
75#include <signal.h>
76#include <stdio.h>
77#include <stdlib.h>
78#include <string.h>
79#include <unistd.h>
80
81#include "krb.h"
82
83#ifndef MAXHOSTNAMELEN
84#define MAXHOSTNAMELEN 64
85#endif
86
87#define	START_PORT	5120	 /* arbitrary */
88
89int	getport __P((int *));
90
91int
92kcmd(sock, ahost, rport, locuser, remuser, cmd, fd2p, ticket, service, realm,
93    cred, schedule, msg_data, laddr, faddr, authopts)
94	int *sock;
95	char **ahost;
96	u_short rport;
97	char *locuser, *remuser, *cmd;
98	int *fd2p;
99	KTEXT ticket;
100	char *service;
101	char *realm;
102	CREDENTIALS *cred;
103	Key_schedule schedule;
104	MSG_DAT *msg_data;
105	struct sockaddr_in *laddr, *faddr;
106	long authopts;
107{
108	int s, timo = 1, pid;
109	long oldmask;
110	struct sockaddr_in sin, from;
111	char c;
112#ifdef ATHENA_COMPAT
113	int lport = IPPORT_RESERVED - 1;
114#else
115	int lport = START_PORT;
116#endif
117	struct hostent *hp;
118	int rc;
119	char *host_save;
120	int status;
121
122	pid = getpid();
123	hp = gethostbyname(*ahost);
124	if (hp == NULL) {
125		/* fprintf(stderr, "%s: unknown host\n", *ahost); */
126		return (-1);
127	}
128
129	host_save = malloc(strlen(hp->h_name) + 1);
130	strcpy(host_save, hp->h_name);
131	*ahost = host_save;
132
133#ifdef KERBEROS
134	/* If realm is null, look up from table */
135	if (realm == NULL || realm[0] == '\0')
136		realm = krb_realmofhost(host_save);
137#endif /* KERBEROS */
138
139	oldmask = sigblock(sigmask(SIGURG));
140	for (;;) {
141		s = getport(&lport);
142		if (s < 0) {
143			if (errno == EAGAIN)
144				fprintf(stderr,
145					"kcmd(socket): All ports in use\n");
146			else
147				perror("kcmd: socket");
148			sigsetmask(oldmask);
149			return (-1);
150		}
151		fcntl(s, F_SETOWN, pid);
152		sin.sin_family = hp->h_addrtype;
153#if defined(ultrix) || defined(sun)
154		bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
155#else
156		bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, hp->h_length);
157#endif
158		sin.sin_port = rport;
159		if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
160			break;
161		(void) close(s);
162		if (errno == EADDRINUSE) {
163			lport--;
164			continue;
165		}
166		/*
167		 * don't wait very long for Kerberos rcmd.
168		 */
169		if (errno == ECONNREFUSED && timo <= 4) {
170			/* sleep(timo); don't wait at all here */
171			timo *= 2;
172			continue;
173		}
174#if !(defined(ultrix) || defined(sun))
175		if (hp->h_addr_list[1] != NULL) {
176			int oerrno = errno;
177
178			fprintf(stderr,
179			    "kcmd: connect to address %s: ",
180			    inet_ntoa(sin.sin_addr));
181			errno = oerrno;
182			perror(NULL);
183			hp->h_addr_list++;
184			bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr,
185			    hp->h_length);
186			fprintf(stderr, "Trying %s...\n",
187				inet_ntoa(sin.sin_addr));
188			continue;
189		}
190#endif /* !(defined(ultrix) || defined(sun)) */
191		if (errno != ECONNREFUSED)
192			perror(hp->h_name);
193		sigsetmask(oldmask);
194		return (-1);
195	}
196	lport--;
197	if (fd2p == 0) {
198		write(s, "", 1);
199		lport = 0;
200	} else {
201		char num[8];
202		int s2 = getport(&lport), s3;
203		int len = sizeof(from);
204
205		if (s2 < 0) {
206			status = -1;
207			goto bad;
208		}
209		listen(s2, 1);
210		(void) sprintf(num, "%d", lport);
211		if (write(s, num, strlen(num) + 1) != strlen(num) + 1) {
212			perror("kcmd(write): setting up stderr");
213			(void) close(s2);
214			status = -1;
215			goto bad;
216		}
217		s3 = accept(s2, (struct sockaddr *)&from, &len);
218		(void) close(s2);
219		if (s3 < 0) {
220			perror("kcmd:accept");
221			lport = 0;
222			status = -1;
223			goto bad;
224		}
225		*fd2p = s3;
226		from.sin_port = ntohs((u_short)from.sin_port);
227		if (from.sin_family != AF_INET ||
228		    from.sin_port >= IPPORT_RESERVED) {
229			fprintf(stderr,
230			 "kcmd(socket): protocol failure in circuit setup.\n");
231			status = -1;
232			goto bad2;
233		}
234	}
235	/*
236	 * Kerberos-authenticated service.  Don't have to send locuser,
237	 * since its already in the ticket, and we'll extract it on
238	 * the other side.
239	 */
240	/* (void) write(s, locuser, strlen(locuser)+1); */
241
242	/* set up the needed stuff for mutual auth, but only if necessary */
243	if (authopts & KOPT_DO_MUTUAL) {
244		int sin_len;
245		*faddr = sin;
246
247		sin_len = sizeof(struct sockaddr_in);
248		if (getsockname(s, (struct sockaddr *)laddr, &sin_len) < 0) {
249			perror("kcmd(getsockname)");
250			status = -1;
251			goto bad2;
252		}
253	}
254#ifdef KERBEROS
255	if ((status = krb_sendauth(authopts, s, ticket, service, *ahost,
256			       realm, (unsigned long) getpid(), msg_data,
257			       cred, schedule,
258			       laddr,
259			       faddr,
260			       "KCMDV0.1")) != KSUCCESS)
261		goto bad2;
262#endif /* KERBEROS */
263
264	(void) write(s, remuser, strlen(remuser)+1);
265	(void) write(s, cmd, strlen(cmd)+1);
266
267	if ((rc = read(s, &c, 1)) != 1) {
268		if (rc == -1)
269			perror(*ahost);
270		else
271			fprintf(stderr,"kcmd: bad connection with remote host\n");
272		status = -1;
273		goto bad2;
274	}
275	if (c != '\0') {
276		while (read(s, &c, 1) == 1) {
277			(void) write(2, &c, 1);
278			if (c == '\n')
279				break;
280		}
281		status = -1;
282		goto bad2;
283	}
284	sigsetmask(oldmask);
285	*sock = s;
286	return (KSUCCESS);
287bad2:
288	if (lport)
289		(void) close(*fd2p);
290bad:
291	(void) close(s);
292	sigsetmask(oldmask);
293	return (status);
294}
295
296int
297getport(alport)
298	int *alport;
299{
300	struct sockaddr_in sin;
301	int s;
302
303	sin.sin_family = AF_INET;
304	sin.sin_addr.s_addr = INADDR_ANY;
305	s = socket(AF_INET, SOCK_STREAM, 0);
306	if (s < 0)
307		return (-1);
308	for (;;) {
309		sin.sin_port = htons((u_short)*alport);
310		if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
311			return (s);
312		if (errno != EADDRINUSE) {
313			(void) close(s);
314			return (-1);
315		}
316		(*alport)--;
317#ifdef ATHENA_COMPAT
318		if (*alport == IPPORT_RESERVED/2) {
319#else
320		if (*alport == IPPORT_RESERVED) {
321#endif
322			(void) close(s);
323			errno = EAGAIN;		/* close */
324			return (-1);
325		}
326	}
327}
328
329#endif /* !NeXT || KERBEROS */
330