summaryrefslogtreecommitdiff
path: root/pm.c
blob: 92818d3526963896aa065ec39381df55f0258bdd (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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
/* $Xorg: pm.c,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */
/*

Copyright "1986-1997, 1998  The Open Group

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.

The above copyright notice and the following permission notice
shall be included in all copies of the Software:

THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN
THE SOFTWARE.

Except as contained in this notice, the name of The Open Group
shall not be used in advertising or otherwise to promote the use
or other dealings in this Software without prior written
authorization from The Open Group.

X Window System is a trademark of The Open Group.

*/
/* $XFree86: xc/programs/xfwp/pm.c,v 1.8tsi Exp $ */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strdup */
#include <X11/Xos.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#ifdef X_POSIX_C_SOURCE
#define _POSIX_C_SOURCE X_POSIX_C_SOURCE
#include <signal.h>
#undef _POSIX_C_SOURCE
#else
#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
#include <signal.h>
#else
#define _POSIX_SOURCE
#include <signal.h>
#undef _POSIX_SOURCE
#endif
#endif

#include <X11/Xproto.h>

#include <X11/ICE/ICElib.h>
#include <X11/ICE/ICEmsg.h>
#include <X11/ICE/ICEproto.h>

#include <X11/PM/PM.h>
#include <X11/PM/PMproto.h>

#include "xfwp.h"
#include "pm.h"
#include "transport.h"
#include "misc.h"

void FWPprocessMessages(
    IceConn iceConn,
    IcePointer * client_data,
    int opcode,
    unsigned long length,
    Bool swap)
{
  switch (opcode)
  {
    /*
     * this is really the only opcode we care about -- the one
     * which indicates an XFindProxy request for a connection
     * to a specified server
     */
    case PM_GetProxyAddr:
    {
      pmGetProxyAddrMsg       	*pMsg;
      char                      *pData, *pStart;
      char                      *serviceName = NULL, *serverAddress = NULL;
      char                      *hostAddress = NULL, *startOptions = NULL;
      char                      *authName = NULL, *authData = NULL;
      int                       authLen;
      struct clientDataStruct * program_data;
      char *			listen_port_string;
      int			pm_send_msg_len;
      pmGetProxyAddrReplyMsg *	pReply;
      char *			pReplyData;
      struct hostent * 		hostptr;
      struct sockaddr_in	server_sockaddr_in;
      struct sockaddr_in	dummy_sockaddr_in;
      char *			server_name_base;
      const char *		config_failure = "unrecognized server or permission denied";
      char *			tmp_str;
      int			rule_number = -1;
      char *			colon;
      char *			tmpAddress = NULL;

      /*
       * this is where we need and get access to that client data we
       * went through such contortions to set up earlier!
       */
      program_data = (struct clientDataStruct *) client_data;

#if 0 /* No-op */
      /*
       * initial check on expected message size
       */
      CHECK_AT_LEAST_SIZE (iceConn, global_data.major_opcode, opcode,
          length, SIZEOF (pmGetProxyAddrMsg), IceFatalToProtocol);
#endif

      IceReadCompleteMessage (iceConn, SIZEOF (pmGetProxyAddrMsg),
          pmGetProxyAddrMsg, pMsg, pStart);

      if (!IceValidIO (iceConn))
      {
        IceDisposeCompleteMessage (iceConn, pStart);
        return;
      }

      authLen = swap ? lswaps (pMsg->authLen) : pMsg->authLen;

      pData = pStart;

      SKIP_STRING (pData, swap);      /* proxy-service */
      SKIP_STRING (pData, swap);      /* server-address */
      SKIP_STRING (pData, swap);      /* host-address */
      SKIP_STRING (pData, swap);      /* start-options */
      if (authLen > 0)
      {
        SKIP_STRING (pData, swap);              /* auth-name */
        pData += (authLen +  PAD64 (authLen));  /* auth-data */
      }
      /*
       * now a detailed check on message size
       */
      CHECK_COMPLETE_SIZE (iceConn, global_data.major_opcode, opcode,
         length, pData - pStart + SIZEOF (pmGetProxyAddrMsg),
         pStart, IceFatalToProtocol);

      pData = pStart;
      /*
       * extract message data, based on known characteristics
       * of this message type
       */
      EXTRACT_STRING (pData, swap, serviceName);
      EXTRACT_STRING (pData, swap, serverAddress);
      EXTRACT_STRING (pData, swap, hostAddress);
      EXTRACT_STRING (pData, swap, startOptions);
      if (authLen > 0)
      {
        EXTRACT_STRING (pData, swap, authName);
        authData = malloc (authLen);
        memcpy (authData, pData, authLen);
      }
#ifdef DEBUG
      (void) fprintf (stderr,
		      "Got GetProxyAddr, serviceName = %s, serverAddr = %s\n",
              	      serviceName, serverAddress);
      (void) fprintf (stderr,
		      "\thostAddr = %s, options = %s, authLen = %d\n",
              	      hostAddress, startOptions, authLen);
      if (authLen > 0)
          (void) fprintf (stderr, "\tauthName = %s\n", authName);
#endif
      /*
       * need to copy the host port string because strtok() changes it
       */
      if ((tmp_str = strdup (serverAddress)) == NULL)
      {
        (void) fprintf(stderr, "malloc - serverAddress copy\n");
        goto sendFailure;
      }

      /*
       * before proceeding we want to verify that we are allowed to
       * accept connections from the host who called xfindproxy();
       * the thing is, we don't get that host name from Proxy Manager
       * even if the "-host <hostname>" command-line option was present
       * in xfindproxy (and even if it was we shouldn't rely on it --
       * much better to have ProxyMngr query the xfindproxy connect
       * socket for its origin); the upshot of all this that we do
       * a configuration check *only* on the destination (which we
       * assume in this case to be the serverAddress passed in by
       * xfindproxy(); so get the destination IP address!
       */
      server_name_base = strtok(tmp_str, ":");
      if ((hostptr = gethostbyname(server_name_base)) == NULL)
      {
        (void) fprintf(stderr, "gethostbyname (%s) failed\n", server_name_base);
	goto sendFailure;
      }
      memset(&server_sockaddr_in, 0, sizeof(server_sockaddr_in));
      memset(&dummy_sockaddr_in, 0, sizeof(dummy_sockaddr_in));
      memcpy(&server_sockaddr_in.sin_addr,
	     hostptr->h_addr,
	     hostptr->h_length);

      /*
       * need to initialize dummy to something, but doesn't matter
       * what (should eventually be the true host address);
       * NOTE:  source configuration will always match (see XFWP man
       * page) unless sysadmin explicitly chooses to deny
       */
      memcpy(&dummy_sockaddr_in.sin_addr,
	     hostptr->h_addr,
	     hostptr->h_length);

      if ((doConfigCheck(&dummy_sockaddr_in,
  			 &server_sockaddr_in,
			 global_data.config_info,
			 FINDPROXY,
			 &rule_number)) == FAILURE)
      {
        (void) fprintf(stderr, "xfindproxy failed config check\n");
      sendFailure:
        /*
         * report failure back to the ProxyMgr
         *
         */
        pm_send_msg_len = STRING_BYTES(config_failure)
	      		+ STRING_BYTES("");
        IceGetHeaderExtra(iceConn,
	      	        program_data->major_opcode,
	      		PM_GetProxyAddrReply,
	       		SIZEOF(pmGetProxyAddrReplyMsg),
	       		WORD64COUNT (pm_send_msg_len),
	       		pmGetProxyAddrReplyMsg,
	       		pReply,
	       		pReplyData);
        pReply->status = PM_Failure;
        STORE_STRING(pReplyData, "");
        STORE_STRING(pReplyData, config_failure);
        IceFlush(iceConn);
	free(tmp_str);
        return;
      }

      /*
       * okay, you got what you need from the PM to proceed,
       * so extract the fd of the selected connection and use
       * it to set up the remote client listen port and add
       * the name of the X server to your list of server connections
       */

      /*
       * Before checking to see if you already have a PM connection
       * request for this server, make serverAddress a
       * FQDN so that synonymous names like oregon:0 and oregon.com:0
       * will be recognized as the same Xserver.  If this server
       * already exists, don't allocate another listen port for it -
       * use the already allocated one
       */
      colon = strchr (serverAddress, ':');
      if (colon)
      {
	  struct hostent *hostent;

	  *colon = '\0';
	  hostent = gethostbyname (serverAddress);
	  *colon = ':';

	  if (hostent && hostent->h_name) {
	      tmpAddress = malloc (strlen (hostent->h_name) +
					    strlen (colon) + 1);
	      (void) sprintf (tmpAddress, "%s%s", hostent->h_name, colon);
	      serverAddress = tmpAddress;
	  }
      }

      if ((doCheckServerList(serverAddress,
			    &listen_port_string,
			    program_data->config_info->num_servers)) == FAILURE)
      {
        /*
         * this server name isn't in your list; so set up a new
         * remote client listen port for it; extract the fd from
         * the connection and pass it in as index to array lookup
         */
        if ((doSetupRemClientListen(&listen_port_string,
			            program_data,
				    serverAddress)) == FAILURE)
 	{
          goto sendFailure;
	}
      }
      if (tmpAddress)
	free (tmpAddress);

      /*
       * the PM-sent server address *was* in your list, so send back
       * the rem client listen port you had already associated with
       * that server (it will presumably be forwarded to the remote
       * client through some other channel)
       * use IceGetHeaderExtra() and the
       */
      pm_send_msg_len = STRING_BYTES(listen_port_string)
			+ STRING_BYTES("");
      IceGetHeaderExtra(iceConn,
		        program_data->major_opcode,
			PM_GetProxyAddrReply,
			SIZEOF(pmGetProxyAddrReplyMsg),
			WORD64COUNT (pm_send_msg_len),
			pmGetProxyAddrReplyMsg,
			pReply,
			pReplyData);
      pReply->status = PM_Success;
      STORE_STRING(pReplyData, listen_port_string);
      STORE_STRING(pReplyData, "");
      IceFlush(iceConn);
      /*
       * before leaving this routine, change the select() timeout
       * here to be equal to the configured client listen timeout
       * (otherwise you'll never *get* to your listen timeout
       * if it's shorter than the startup select() default
       */
      program_data->config_info->select_timeout.tv_sec =
		program_data->config_info->client_listen_timeout;
      break;
    }

    case ICE_Error:
    {
	iceErrorMsg *pMsg;
	char *pStart;

	CHECK_AT_LEAST_SIZE (iceConn, global_data.major_opcode, ICE_Error,
			     length, sizeof(iceErrorMsg), IceFatalToProtocol);

	IceReadCompleteMessage (iceConn, SIZEOF (iceErrorMsg),
				iceErrorMsg, pMsg, pStart);

	if (!IceValidIO (iceConn))
	{
	    IceDisposeCompleteMessage (iceConn, pStart);
	    return;
	}

	if (swap)
	{
	    pMsg->errorClass = lswaps (pMsg->errorClass);
	    pMsg->offendingSequenceNum = lswapl (pMsg->offendingSequenceNum);
	}

	(void) fprintf(stderr, "Proxy Manager reported ICE Error:\n");
	(void) fprintf(stderr, "\tclass = 0x%x, offending minor opcode = %d\n",
		       pMsg->errorClass, pMsg->offendingMinorOpcode);
	(void) fprintf(stderr, "\tseverity = %d, sequence = %ld\n",
		       pMsg->severity, (long)pMsg->offendingSequenceNum);

	IceDisposeCompleteMessage (iceConn, pStart);

	break;
    }

    default:
      break;
  } /* end switch */
}


/*ARGSUSED*/
Bool
FWPHostBasedAuthProc (
    char * hostname)
{
  /*
   * don't worry about config for now
   *
   * this routine gets called *after* IceAcceptConnection
   * is called but *before* that routine returns its status;
   * it is therefore the logical place to check configuration
   * data on which PM connections (from which hosts) will be
   * accepted; so do it and return either 0 to terminate
   * connection (automatically informing PM) or 1 to proceed
   *
   * the PM host is not allowed; terminate connection and inform
   * requestor why (handled automatically by ICElib)
   */

  return True;
}


/*ARGSUSED*/
Status
FWPprotocolSetupProc(
    IceConn iceConn,
    int major_version,
    int minor_version,
    char * vendor,
    char * release,
    IcePointer * clientDataRet,
    char ** failureReasonRet)
{
  /*
   * This routine gets invoked when the remote ICE originator
   * (in this case PM) calls IceRegisterSetup() on *its* end
   * of the FWP connection; this is where the pointer to
   * client data should be initialized, so that this data
   * can be accessed when FMprocessMessages is called by
   * IceProcessMessages()
   */
  struct clientDataStruct * 	client_data;
  if ((client_data = malloc (sizeof (struct clientDataStruct))) == NULL)
  {
    (void) fprintf(stderr, "malloc - client data object\n");
    return (0);
  }
  /*
   * setup the client data struct; we need this object in order to
   * avoid making these variables global so they can be accessed in the
   * ICE FWPprocessMessages() callback; now you see that our global_data
   * struct was the only way of getting program data into the
   * protocolReply setup routine!
   */
  client_data->config_info = global_data.config_info;
  client_data->nfds = global_data.nfds;
  client_data->rinit = global_data.rinit;
  client_data->winit = global_data.winit;
  client_data->major_opcode = global_data.major_opcode;
  *clientDataRet = client_data;

  return (1);
}

int
doSetupPMListen(
    char 		*  pm_port,
    int			* size_pm_listen_array,
    int 		** pm_listen_array,
    IceListenObj 	** listen_objects,
    int 		* nfds,
    fd_set 		* rinit)
{
  int 	num_fds_returned;
  char  errormsg[256];
  int   fd_counter;
  IceListenObj * temp_obj;

  /*
   * establish PM listeners
   */
  if (!IceListenForWellKnownConnections(pm_port,
					&num_fds_returned,
					listen_objects,
					256,
					errormsg))
  {
    (void) fprintf(stderr, "IceListenForWellKnownConnections error: %s\n",
		   errormsg);
    return 0;
  }

  /*
   * Create space for pm_listen_array
   */
  *pm_listen_array = malloc (num_fds_returned * sizeof (int *));
  if (!pm_listen_array)
  {
    (void) fprintf (stderr, "malloc - pm_listen_array\n");
    return 0;
  }
  *size_pm_listen_array = num_fds_returned;

  /*
   * obtain the PM listen fd's for the connection objects
   */
  for (fd_counter = 0; fd_counter < num_fds_returned; fd_counter++)
  {
    /*
     * get fd(s) for PM listen (could be more than one if different
     * transport mechanisms)
     */
    temp_obj = *listen_objects;
    IceSetHostBasedAuthProc(temp_obj[fd_counter], FWPHostBasedAuthProc);
    (*pm_listen_array)[fd_counter] =
     		IceGetListenConnectionNumber(temp_obj[fd_counter]);

    /*
     * set all read mask bits on which we are going to select();
     * [NOTE:  We don't care about write bits here because we don't
     * use select() to manage writing to the PM]
     */
    FD_SET((*pm_listen_array)[fd_counter], rinit);

    /*
     * compute nfds for select()
     */
    *nfds = max(*nfds, (*pm_listen_array)[fd_counter] + 1);
  }
  return 1;
}


/*
 * The real way to handle IO errors is to check the return status
 * of IceProcessMessages.  xsm properly does this.
 *
 * Unfortunately, a design flaw exists in the ICE library in which
 * a default IO error handler is invoked if no IO error handler is
 * installed.  This default handler exits.  We must avoid this.
 *
 * To get around this problem, we install an IO error handler that
 * does a little magic.  Since a previous IO handler might have been
 * installed, when we install our IO error handler, we do a little
 * trick to get both the previous IO error handler and the default
 * IO error handler.  When our IO error handler is called, if the
 * previous handler is not the default handler, we call it.  This
 * way, everyone's IO error handler gets called except the stupid
 * default one which does an exit!
 */

static IceIOErrorHandler prev_handler;

static void
MyIoErrorHandler (
    IceConn ice_conn)

{
    if (prev_handler)
        (*prev_handler) (ice_conn);
}

void
doInstallIOErrorHandler (void)
{
    IceIOErrorHandler default_handler;

    prev_handler = IceSetIOErrorHandler (NULL);
    default_handler = IceSetIOErrorHandler (MyIoErrorHandler);
    if (prev_handler == default_handler)
        prev_handler = NULL;
#ifdef X_NOT_POSIX
    signal(SIGPIPE, SIG_IGN);
#else
    {
        struct sigaction act;

        (void) sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        act.sa_handler = SIG_IGN;
        (void) sigaction(SIGPIPE, &act, NULL);
    }
#endif
}