c3d0263773be4a971ebfce14a5b24eee55512f12
[platform/upstream/krb5.git] / src / util / k5test.py
1 # Copyright (C) 2010 by the Massachusetts Institute of Technology.
2 # All rights reserved.
3
4 # Export of this software from the United States of America may
5 #   require a specific license from the United States Government.
6 #   It is the responsibility of any person or organization contemplating
7 #   export to obtain such a license before exporting.
8 #
9 # WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10 # distribute this software and its documentation for any purpose and
11 # without fee is hereby granted, provided that the above copyright
12 # notice appear in all copies and that both that copyright notice and
13 # this permission notice appear in supporting documentation, and that
14 # the name of M.I.T. not be used in advertising or publicity pertaining
15 # to distribution of the software without specific, written prior
16 # permission.  Furthermore if you modify this software you must label
17 # your software as modified software and not distribute it in such a
18 # fashion that it might be confused with the original M.I.T. software.
19 # M.I.T. makes no representations about the suitability of
20 # this software for any purpose.  It is provided "as is" without express
21 # or implied warranty.
22
23 """A module for krb5 test scripts
24
25 To run test scripts during "make check" (if Python 2.5 or later is
26 available), add rules like the following to Makefile.in:
27
28     check-pytests::
29         $(RUNPYTEST) $(srcdir)/t_testname.py $(PYTESTFLAGS)
30
31 A sample test script:
32
33     from k5test import *
34
35     # Run a test program under a variety of configurations:
36     for realm in multipass_realms():
37         realm.run(['./testprog', 'arg'])
38
39     # Run a test server and client under just the default configuration:
40     realm = K5Realm()
41     realm.start_server(['./serverprog'], 'starting...')
42     realm.run(['./clientprog', realm.host_princ])
43
44     # Inform framework that tests completed successfully.
45     success('World peace and cure for cancer')
46
47 By default, the realm will have:
48
49 * The name KRBTEST.COM
50 * Listener ports starting at 61000
51 * krb5.conf and kdc.conf files
52 * A fresh DB2 KDB
53 * Running krb5kdc (but not kadmind)
54 * Principals named realm.user_princ and realm.admin_princ; call
55   password('user') and password('admin') to get the password
56 * Credentials for realm.user_princ in realm.ccache
57 * Admin rights for realm.admin_princ in the kadmind acl file
58 * A host principal named realm.host_princ with a random key
59 * A keytab for the host principal in realm.keytab
60
61 The realm's behaviour can be modified with the following constructor
62 keyword arguments:
63
64 * realm='realmname': Override the realm name
65
66 * portbase=NNN: Override the listener port base; currently three ports are
67   used
68
69 * testdir='dirname': Override the storage area for the realm's files
70   (path may be specified relative to the current working dir)
71
72 * krb5_conf={ ... }: krb5.conf options, expressed as a nested
73   dictionary, to be merged with the default krb5.conf settings.  A key
74   may be mapped to None to delete a setting from the defaults.  A key
75   may be mapped to a list in order to create multiple settings for the
76   same variable name.  Keys and values undergo the following template
77   substitutions:
78
79     - $realm:    The realm name
80     - $testdir:  The realm storage directory (absolute path)
81     - $buildtop: The root of the build directory
82     - $srctop:   The root of the source directory
83     - $plugins:  The plugin directory in the build tree
84     - $hostname: The FQDN of the host
85     - $port0:    The first listener port (portbase)
86     - ...
87     - $port9:    The tenth listener port (portbase + 9)
88
89   When choosing ports, note the following:
90
91     - port0 is used in the default krb5.conf for the KDC
92     - port1 is used in the default krb5.conf for kadmind
93     - port2 is used in the default krb5.conf for kpasswd
94     - port3 is used in the default krb5.conf for kpropd
95     - port4 is used in the default krb5.conf for iprop (in kadmind)
96     - port5 is the return value of realm.server_port()
97
98 * kdc_conf={...}: kdc.conf options, expressed as a nested dictionary,
99   to be merged with the default kdc.conf settings.  The same
100   conventions and substitutions for krb5_conf apply.
101
102 * create_kdb=False: Don't create a KDB.  Implicitly disables all of
103   the other options since they all require a KDB.
104
105 * krbtgt_keysalt='enctype:salttype': After creating the KDB,
106   regenerate the krbtgt key using the specified key/salt combination,
107   using a kadmin.local cpw query.
108
109 * create_user=False: Don't create the user principal.  Implies
110   get_creds=False.
111
112 * create_host=False: Don't create the host principal or the associated
113   keytab.
114
115 * start_kdc=False: Don't start the KDC.  Implies get_creds=False.
116
117 * start_kadmind=True: Start kadmind.
118
119 * get_creds=False: Don't get user credentials.
120
121 Scripts may use the following functions and variables:
122
123 * fail(message): Display message (plus leading marker and trailing
124   newline) and explanatory messages about debugging.
125
126 * success(message): Indicate that the test script has completed
127   successfully.  Suppresses the display of explanatory debugging
128   messages in the on-exit handler.  message should briefly summarize
129   the operations tested; it will only be displayed (with leading
130   marker and trailing newline) if the script is running verbosely.
131
132 * skipped(whatmsg, whymsg): Indicate that some tests were skipped.
133   whatmsg should concisely say what was skipped (e.g. "LDAP KDB
134   tests") and whymsg should give the reason (e.g. "because LDAP module
135   not built").
136
137 * skip_rest(message): Indicate that some tests were skipped, then exit
138   the current script.
139
140 * output(message, force_verbose=False): Place message (without any
141   added newline) in testlog, and write it to stdout if running
142   verbosely.
143
144 * which(progname): Return the location of progname in the executable
145   path, or None if it is not found.
146
147 * password(name): Return a weakly random password based on name.  The
148   password will be consistent across calls with the same name.
149
150 * stop_daemon(proc): Stop a daemon process started with
151   realm.start_server() or realm.start_in_inetd().  Only necessary if
152   the port needs to be reused; daemon processes will be stopped
153   automatically when the script exits.
154
155 * multipass_realms(**keywords): This is an iterator function.  Yields
156   a realm for each of the standard test passes, each of which alters
157   the default configuration in some way to exercise different parts of
158   the krb5 code base.  keywords may contain any K5Realm initializer
159   keyword with the exception of krbtgt_keysalt, which will not be
160   honored.  If keywords contains krb5_conf and/or kdc_conf fragments,
161   they will be merged with the default and per-pass specifications.
162
163 * cross_realms(num, xtgts=None, args=None, **keywords): This function
164   returns a list of num realms, where each realm's configuration knows
165   how to contact all of the realms.  By default, each realm will
166   contain cross TGTs in both directions for all other realms; this
167   default may be overridden by specifying a collection of tuples in
168   the xtgts parameter, where each tuple is a pair of zero-based realm
169   indexes, indicating that the first realm can authenticate to the
170   second (i.e. krbtgt/secondrealm@firstrealm exists in both realm's
171   databases).  If args is given, it should be a list of keyword
172   arguments specific to each realm; these will be merged with the
173   global keyword arguments passed to cross_realms, with specific
174   arguments taking priority.
175
176 * buildtop: The top of the build directory (absolute path).
177
178 * srctop: The top of the source directory (absolute path).
179
180 * plugins: The plugin directory in the build tree (absolute path).
181
182 * hostname: This machine's fully-qualified domain name.
183
184 * null_input: A file opened to read /dev/null.
185
186 * args: Positional arguments left over after flags are processed.
187
188 * runenv: The contents of $srctop/runenv.py, containing a dictionary
189   'env' which specifies additional variables to be added to the realm
190   environment, and a variable 'tls_impl', which indicates which TLS
191   implementation (if any) is being used by libkrb5's support for
192   contacting KDCs and kpasswd servers over HTTPS.
193
194 * verbose: Whether the script is running verbosely.
195
196 * testpass: The command-line test pass argument.  The script does not
197   need to examine this argument in most cases; it will be honored in
198   multipass_realms().
199
200 * Pathname variables for programs within the build directory:
201   - krb5kdc
202   - kadmind
203   - kadmin
204   - kadminl (kadmin.local)
205   - kdb5_ldap_util
206   - kdb5_util
207   - ktutil
208   - kinit
209   - klist
210   - kswitch
211   - kvno
212   - kdestroy
213   - kpasswd
214   - t_inetd
215   - kproplog
216   - kpropd
217   - kprop
218
219 Scripts may use the following realm methods and attributes:
220
221 * realm.run(args, env=None, **keywords): Run a command in a specified
222   environment (or the realm's environment by default), obeying the
223   command-line debugging options.  Fail if the command does not return
224   0.  Log the command output appropriately, and return it as a single
225   multi-line string.  Keyword arguments can contain input='string' to
226   send an input string to the command, and expected_code=N to expect a
227   return code other than 0.
228
229 * realm.kprop_port(): Returns a port number based on realm.portbase
230   intended for use by kprop and kpropd.
231
232 * realm.server_port(): Returns a port number based on realm.portbase
233   intended for use by server processes.
234
235 * realm.start_server(args, sentinel, env=None): Start a daemon
236   process.  Wait until sentinel appears as a substring of a line in
237   the server process's stdout or stderr (which are folded together).
238   Returns a subprocess.Popen object which can be passed to
239   stop_daemon() to stop the server, or used to read from the server's
240   output.
241
242 * realm.start_in_inetd(args, port=None, env=None): Begin a t_inetd
243   process which will spawn a server process after accepting a client
244   connection.  If port is not specified, realm.server_port() will be
245   used.  Returns a process object which can be passed to stop_daemon()
246   to stop the server.
247
248 * realm.create_kdb(): Create a new KDB.
249
250 * realm.start_kdc(args=[], env=None): Start a krb5kdc process.  Errors
251   if a KDC is already running.  If args is given, it contains a list
252   of additional krb5kdc arguments.
253
254 * realm.stop_kdc(): Stop the krb5kdc process.  Errors if no KDC is
255   running.
256
257 * realm.start_kadmind(env=None): Start a kadmind process.  Errors if a
258   kadmind is already running.
259
260 * realm.stop_kadmind(): Stop the kadmind process.  Errors if no
261   kadmind is running.
262
263 * realm.stop(): Stop any daemon processes running on behalf of the
264   realm.
265
266 * realm.addprinc(princname, password=None): Using kadmin.local, create
267   a principal in the KDB named princname, with either a random or
268   specified key.
269
270 * realm.extract_keytab(princname, keytab): Using kadmin.local, create
271   a keytab for princname in the filename keytab.  Uses the -norandkey
272   option to avoid re-randomizing princname's key.
273
274 * realm.kinit(princname, password=None, flags=[]): Acquire credentials
275   for princname using kinit, with additional flags [].  If password is
276   specified, it will be used as input to the kinit process; otherwise
277   flags must cause kinit not to need a password (e.g. by specifying a
278   keytab).
279
280 * realm.klist(client_princ, service_princ=None, ccache=None): Using
281   klist, list the credentials cache ccache (must be a filename;
282   self.ccache if not specified) and verify that the output shows
283   credentials for client_princ and service_princ (self.krbtgt_princ if
284   not specified).
285
286 * realm.klist_keytab(princ, keytab=None): Using klist, list keytab
287   (must be a filename; self.keytab if not specified) and verify that
288   the output shows the keytab name and principal name.
289
290 * realm.prep_kadmin(princname=None, password=None, flags=[]): Populate
291   realm.kadmin_ccache with a ticket which can be used to run kadmin.
292   If princname is not specified, realm.admin_princ and its default
293   password will be used.
294
295 * realm.run_kadmin(args, **keywords): Run the specified query in
296   kadmin, using realm.kadmin_ccache to authenticate.  Accepts the same
297   keyword arguments as run.
298
299 * realm.special_env(name, has_kdc_conf, krb5_conf=None,
300   kdc_conf=None): Create an environment with a modified krb5.conf
301   and/or kdc.conf.  The specified krb5_conf and kdc_conf fragments, if
302   any, will be merged with the realm's existing configuration.  If
303   has_kdc_conf is false, the new environment will have no kdc.conf.
304   The environment returned by this method can be used with realm.run()
305   or similar methods.
306
307 * realm.start_kpropd(env, args=[]): Start a kpropd process.  Pass an
308   environment created with realm.special_env() for the slave.  If args
309   is given, it contains a list of additional kpropd arguments.
310   Returns a handle to the kpropd process.
311
312 * realm.run_kpropd_once(env, args=[]): Run kpropd once, using the -t
313   flag.  Pass an environment created with realm.special_env() for the
314   slave.  If args is given, it contains a list of additional kpropd
315   arguments.  Returns the kpropd output.
316
317 * realm.realm: The realm's name.
318
319 * realm.testdir: The realm's storage directory (absolute path).
320
321 * realm.portbase: The realm's first listener port.
322
323 * realm.user_princ: The principal name user@<realmname>.
324
325 * realm.admin_princ: The principal name user/admin@<realmname>.
326
327 * realm.host_princ: The name of the host principal for this machine,
328   with realm.
329
330 * realm.nfs_princ: The name of the nfs principal for this machine,
331   with realm.
332
333 * realm.krbtgt_princ: The name of the krbtgt principal for the realm.
334
335 * realm.keytab: A keytab file in realm.testdir.  Initially contains a
336   host keytab unless disabled by the realm construction options.
337
338 * realm.client_keytab: A keytab file in realm.testdir.  Initially
339   nonexistent.
340
341 * realm.ccache: A ccache file in realm.testdir.  Initially contains
342   credentials for user unless disabled by the realm construction
343   options.
344
345 * realm.kadmin_ccache: The ccache file initialized by prep_kadmin and
346   used by run_kadmin.
347
348 * env: The realm's environment, extended from os.environ to point at
349   the realm's config files and the build tree's shared libraries.
350
351 When the test script is run, its behavior can be modified with
352 command-line flags.  These are documented in the --help output.
353
354 """
355
356 import atexit
357 import optparse
358 import os
359 import shlex
360 import shutil
361 import signal
362 import socket
363 import string
364 import subprocess
365 import sys
366 import imp
367
368 # Used when most things go wrong (other than programming errors) so
369 # that the user sees an error message rather than a Python traceback,
370 # without help from the test script.  The on-exit handler will display
371 # additional explanatory text.
372 def fail(msg):
373     """Print a message and exit with failure."""
374     global _current_pass
375     print "*** Failure:", msg
376     if _last_cmd:
377         print "*** Last command (#%d): %s" % (_cmd_index - 1, _last_cmd)
378     if _last_cmd_output:
379         print "*** Output of last command:"
380         sys.stdout.write(_last_cmd_output)
381     if _current_pass:
382         print "*** Failed in test pass:", _current_pass
383     sys.exit(1)
384
385
386 def success(msg):
387     global _success
388     output('*** Success: %s\n' % msg)
389     _success = True
390
391
392 def skipped(whatmsg, whymsg):
393     output('*** Skipping: %s: %s\n' % (whatmsg, whymsg), force_verbose=True)
394     f = open(os.path.join(buildtop, 'skiptests'), 'a')
395     f.write('Skipped %s: %s\n' % (whatmsg, whymsg))
396     f.close()
397
398
399 def skip_rest(whatmsg, whymsg):
400     global _success
401     skipped(whatmsg, whymsg)
402     _success = True
403     sys.exit(0)
404
405
406 def output(msg, force_verbose=False):
407     """Output a message to testlog, and to stdout if running verbosely."""
408     _outfile.write(msg)
409     if verbose or force_verbose:
410         sys.stdout.write(msg)
411
412
413 # Return the location of progname in the executable path, or None if
414 # it is not found.
415 def which(progname):
416     for dir in os.environ["PATH"].split(os.pathsep):
417         path = os.path.join(dir, progname)
418         if os.access(path, os.X_OK):
419             return path
420     return None
421
422
423 def password(name):
424     """Choose a weakly random password from name, consistent across calls."""
425     return name + str(os.getpid())
426
427
428 # Exit handler which ensures processes are cleaned up and, on failure,
429 # prints messages to help developers debug the problem.
430 def _onexit():
431     global _daemons, _success, srctop, verbose
432     global _debug, _stop_before, _stop_after, _shell_before, _shell_after
433     if _daemons is None:
434         # In Python 2.5, if we exit as a side-effect of importing
435         # k5test, _onexit will execute in an empty global namespace.
436         # This can happen if argument processing fails or the build
437         # root isn't valid.  In this case we can safely assume that no
438         # daemons have been launched and that we don't really need to
439         # amend the error message.  The bug is fixed in Python 2.6.
440         return
441     if _debug or _stop_before or _stop_after or _shell_before or _shell_after:
442         # Wait before killing daemons in case one is being debugged.
443         sys.stdout.write('*** Press return to kill daemons and exit script: ')
444         sys.stdin.readline()
445     for proc in _daemons:
446         os.kill(proc.pid, signal.SIGTERM)
447     if not _success:
448         print
449         if not verbose:
450             testlogfile = os.path.join(os.getcwd(), 'testlog')
451             utildir = os.path.join(srctop, 'util')
452             print 'For details, see: %s' % testlogfile
453             print 'Or re-run this test script with the -v flag:'
454             print '    cd %s' % os.getcwd()
455             print '    PYTHONPATH=%s %s %s -v' % \
456                 (utildir, sys.executable, sys.argv[0])
457             print
458         print 'Use --debug=NUM to run a command under a debugger.  Use'
459         print '--stop-after=NUM to stop after a daemon is started in order to'
460         print 'attach to it with a debugger.  Use --help to see other options.'
461
462
463 def _onsigint(signum, frame):
464     # Exit without displaying a stack trace.  Suppress messages from _onexit.
465     global _success
466     _success = True
467     sys.exit(1)
468
469
470 # Find the parent of dir which is at the root of a build or source directory.
471 def _find_root(dir):
472     while True:
473         if os.path.exists(os.path.join(dir, 'lib', 'krb5', 'krb')):
474             break
475         parent = os.path.dirname(dir)
476         if (parent == dir):
477             return None
478         dir = parent
479     return dir
480
481
482 def _find_buildtop():
483     root = _find_root(os.getcwd())
484     if root is None:
485         fail('Cannot find root of krb5 build directory.')
486     if not os.path.exists(os.path.join(root, 'config.status')):
487         # Looks like an unbuilt source directory.
488         fail('This script must be run inside a krb5 build directory.')
489     return root
490
491
492 def _find_srctop():
493     scriptdir = os.path.abspath(os.path.dirname(sys.argv[0]))
494     if not scriptdir:
495         scriptdir = os.getcwd()
496     root = _find_root(scriptdir)
497     if root is None:
498         fail('Cannot find root of krb5 source directory.')
499     return os.path.abspath(root)
500
501
502 # Return the local hostname as it will be canonicalized by
503 # krb5_sname_to_principal.  We can't simply use socket.getfqdn()
504 # because it explicitly prefers results containing periods and
505 # krb5_sname_to_principal doesn't care.
506 def _get_hostname():
507     hostname = socket.gethostname()
508     try:
509         ai = socket.getaddrinfo(hostname, None, 0, 0, 0, socket.AI_CANONNAME)
510     except socket.gaierror, (error, errstr):
511         fail('Local hostname "%s" does not resolve: %s.' % (hostname, errstr))
512     (family, socktype, proto, canonname, sockaddr) = ai[0]
513     try:
514         name = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD)
515     except socket.gaierror:
516         return canonname.lower()
517     return name[0].lower()
518
519 # Parse command line arguments, setting global option variables.  Also
520 # sets the global variable args to the positional arguments, which may
521 # be used by the test script.
522 def _parse_args():
523     global args, verbose, testpass, _debug, _debugger_command
524     global _stop_before, _stop_after, _shell_before, _shell_after
525     parser = optparse.OptionParser()
526     parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
527                       default=False, help='Display verbose output')
528     parser.add_option('-p', '--pass', dest='testpass', metavar='PASS',
529                       help='If a multi-pass test, run only PASS')
530     parser.add_option('--debug', dest='debug', metavar='NUM',
531                       help='Debug numbered command (or "all")')
532     parser.add_option('--debugger', dest='debugger', metavar='COMMAND',
533                       help='Debugger command (default is gdb --args)',
534                       default='gdb --args')
535     parser.add_option('--stop-before', dest='stopb', metavar='NUM',
536                       help='Stop before numbered command (or "all")')
537     parser.add_option('--stop-after', dest='stopa', metavar='NUM',
538                       help='Stop after numbered command (or "all")')
539     parser.add_option('--shell-before', dest='shellb', metavar='NUM',
540                       help='Spawn shell before numbered command (or "all")')
541     parser.add_option('--shell-after', dest='shella', metavar='NUM',
542                       help='Spawn shell after numbered command (or "all")')
543     (options, args) = parser.parse_args()
544     verbose = options.verbose
545     testpass = options.testpass
546     _debug = _parse_cmdnum('--debug', options.debug)
547     _debugger_command = shlex.split(options.debugger)
548     _stop_before = _parse_cmdnum('--stop-before', options.stopb)
549     _stop_after = _parse_cmdnum('--stop-after', options.stopa)
550     _shell_before = _parse_cmdnum('--shell-before', options.shellb)
551     _shell_after = _parse_cmdnum('--shell-after', options.shella)
552
553
554 # Translate a command number spec.  -1 means all, None means none.
555 def _parse_cmdnum(optname, str):
556     if not str:
557         return None
558     if str == 'all':
559         return -1
560     try:
561         return int(str)
562     except ValueError:
563         fail('%s value must be "all" or a number' % optname)
564
565
566 # Test if a command index matches a translated command number spec.
567 def _match_cmdnum(cmdnum, ind):
568     if cmdnum is None:
569         return False
570     elif cmdnum == -1:
571         return True
572     else:
573         return cmdnum == ind
574
575
576 # Return an environment suitable for running programs in the build
577 # tree.  It is safe to modify the result.
578 def _build_env():
579     global buildtop, runenv
580     env = os.environ.copy()
581     for (k, v) in runenv.env.iteritems():
582         if v.find('./') == 0:
583             env[k] = os.path.join(buildtop, v)
584         else:
585             env[k] = v
586     # Make sure we don't get confused by translated messages
587     # or localized times.
588     env['LC_ALL'] = 'C'
589     return env
590
591
592 def _import_runenv():
593     global buildtop
594     runenv_py = os.path.join(buildtop, 'runenv.py')
595     if not os.path.exists(runenv_py):
596         fail('You must run "make runenv.py" in %s first.' % buildtop)
597     return imp.load_source('runenv', runenv_py)
598
599
600 # Merge the nested dictionaries cfg1 and cfg2 into a new dictionary.
601 # cfg1 or cfg2 may be None, in which case the other is returned.  If
602 # cfg2 contains keys mapped to None, the corresponding keys will be
603 # mapped to None in the result.  The result may contain references to
604 # parts of cfg1 or cfg2, so is not safe to modify.
605 def _cfg_merge(cfg1, cfg2):
606     if not cfg2:
607         return cfg1
608     if not cfg1:
609         return cfg2
610     result = cfg1.copy()
611     for key, value2 in cfg2.items():
612         if value2 is None or key not in result:
613             result[key] = value2
614         else:
615             value1 = result[key]
616             if isinstance(value1, dict):
617                 if not isinstance(value2, dict):
618                     raise TypeError()
619                 result[key] = _cfg_merge(value1, value2)
620             else:
621                 result[key] = value2
622     return result
623
624
625 # Python gives us shlex.split() to turn a shell command into a list of
626 # arguments, but oddly enough, not the easier reverse operation.  For
627 # now, do a bad job of faking it.
628 def _shell_equiv(args):
629     return " ".join(args)
630
631
632 # Add a valgrind prefix to the front of args if specified in the
633 # environment.  Under normal circumstances this just returns args.
634 def _valgrind(args):
635     valgrind = os.getenv('VALGRIND')
636     if valgrind:
637         args = shlex.split(valgrind) + args
638     return args
639
640
641 def _stop_or_shell(stop, shell, env, ind):
642     if (_match_cmdnum(stop, ind)):
643         sys.stdout.write('*** [%d] Waiting for return: ' % ind)
644         sys.stdin.readline()
645     if (_match_cmdnum(shell, ind)):
646         output('*** [%d] Spawning shell\n' % ind, True)
647         subprocess.call(os.getenv('SHELL'), env=env)
648
649
650 def _run_cmd(args, env, input=None, expected_code=0):
651     global null_input, _cmd_index, _last_cmd, _last_cmd_output, _debug
652     global _stop_before, _stop_after, _shell_before, _shell_after
653
654     if (_match_cmdnum(_debug, _cmd_index)):
655         return _debug_cmd(args, env, input)
656
657     args = _valgrind(args)
658     _last_cmd = _shell_equiv(args)
659
660     output('*** [%d] Executing: %s\n' % (_cmd_index, _last_cmd))
661     _stop_or_shell(_stop_before, _shell_before, env, _cmd_index)
662
663     if input:
664         infile = subprocess.PIPE
665     else:
666         infile = null_input
667
668     # Run the command and log the result, folding stderr into stdout.
669     proc = subprocess.Popen(args, stdin=infile, stdout=subprocess.PIPE,
670                             stderr=subprocess.STDOUT, env=env)
671     (outdata, dummy_errdata) = proc.communicate(input)
672     _last_cmd_output = outdata
673     code = proc.returncode
674     output(outdata)
675     output('*** [%d] Completed with return code %d\n' % (_cmd_index, code))
676     _stop_or_shell(_stop_after, _shell_after, env, _cmd_index)
677     _cmd_index += 1
678
679     # Check the return code and return the output.
680     if code != expected_code:
681         fail('%s failed with code %d.' % (args[0], code))
682     return outdata
683
684
685 def _debug_cmd(args, env, input):
686     global _cmd_index, _debugger_command
687
688     args = _debugger_command + list(args)
689     output('*** [%d] Executing in debugger: %s\n' %
690            (_cmd_index, _shell_equiv(args)), True)
691     if input:
692         print
693         print '*** Enter the following input when appropriate:'
694         print 
695         print input
696         print
697     code = subprocess.call(args, env=env)
698     output('*** [%d] Completed in debugger with return code %d\n' %
699            (_cmd_index, code))
700     _cmd_index += 1
701
702
703 # Start a daemon process with the specified args and env.  Wait until
704 # we see sentinel as a substring of a line on either stdout or stderr.
705 # Clean up the daemon process on exit.
706 def _start_daemon(args, env, sentinel):
707     global null_input, _cmd_index, _last_cmd, _last_cmd_output, _debug
708     global _stop_before, _stop_after, _shell_before, _shell_after
709
710     if (_match_cmdnum(_debug, _cmd_index)):
711         output('*** [%d] Warning: ' % _cmd_index, True)
712         output( 'test script cannot proceed after debugging a daemon\n', True)
713         _debug_cmd(args, env, None)
714         output('*** Exiting after debugging daemon\n', True)
715         sys.exit(1)
716
717     args = _valgrind(args)
718     _last_cmd = _shell_equiv(args)
719     output('*** [%d] Starting: %s\n' % (_cmd_index, _last_cmd))
720     _stop_or_shell(_stop_before, _shell_before, env, _cmd_index)
721
722     # Start the daemon and look for the sentinel in stdout or stderr.
723     proc = subprocess.Popen(args, stdin=null_input, stdout=subprocess.PIPE,
724                             stderr=subprocess.STDOUT, env=env)
725     _last_cmd_output = ''
726     while True:
727         line = proc.stdout.readline()
728         _last_cmd_output += line
729         if line == "":
730             code = proc.wait()
731             fail('%s failed to start with code %d.' % (args[0], code))
732         output(line)
733         if sentinel in line:
734             break
735     output('*** [%d] Started with pid %d\n' % (_cmd_index, proc.pid))
736     _stop_or_shell(_stop_after, _shell_after, env, _cmd_index)
737     _cmd_index += 1
738
739     # Save the daemon in a list for cleanup.  Note that we won't read
740     # any more of the daemon's output after the sentinel, which will
741     # cause the daemon to block if it generates enough.  For now we
742     # assume all daemon processes are quiet enough to avoid this
743     # problem.  If it causes an issue, some alternatives are:
744     #   - Output to a file and poll the file for the sentinel
745     #     (undesirable because it slows down the test suite by the
746     #     polling interval times the number of daemons started)
747     #   - Create an intermediate subprocess which discards output
748     #     after the sentinel.
749     _daemons.append(proc)
750
751     # Return the process; the caller can stop it with stop_daemon.
752     return proc
753
754
755 def stop_daemon(proc):
756     output('*** Terminating process %d\n' % proc.pid)
757     os.kill(proc.pid, signal.SIGTERM)
758     proc.wait()
759     _daemons.remove(proc)
760
761
762 class K5Realm(object):
763     """An object representing a functional krb5 test realm."""
764
765     def __init__(self, realm='KRBTEST.COM', portbase=61000, testdir='testdir',
766                  krb5_conf=None, kdc_conf=None, create_kdb=True,
767                  krbtgt_keysalt=None, create_user=True, get_creds=True,
768                  create_host=True, start_kdc=True, start_kadmind=False,
769                  start_kpropd=False):
770         global hostname, _default_krb5_conf, _default_kdc_conf
771
772         self.realm = realm
773         self.testdir = os.path.join(os.getcwd(), testdir)
774         self.portbase = portbase
775         self.user_princ = 'user@' + self.realm
776         self.admin_princ = 'user/admin@' + self.realm
777         self.host_princ = 'host/%s@%s' % (hostname, self.realm)
778         self.nfs_princ = 'nfs/%s@%s' % (hostname, self.realm)
779         self.krbtgt_princ = 'krbtgt/%s@%s' % (self.realm, self.realm)
780         self.keytab = os.path.join(self.testdir, 'keytab')
781         self.client_keytab = os.path.join(self.testdir, 'client_keytab')
782         self.ccache = os.path.join(self.testdir, 'ccache')
783         self.kadmin_ccache = os.path.join(self.testdir, 'kadmin_ccache')
784         self._krb5_conf = _cfg_merge(_default_krb5_conf, krb5_conf)
785         self._kdc_conf = _cfg_merge(_default_kdc_conf, kdc_conf)
786         self._kdc_proc = None
787         self._kadmind_proc = None
788         self._kpropd_procs = []
789         krb5_conf_path = os.path.join(self.testdir, 'krb5.conf')
790         kdc_conf_path = os.path.join(self.testdir, 'kdc.conf')
791         self.env = self._make_env(krb5_conf_path, kdc_conf_path)
792
793         self._create_empty_dir()
794         self._create_conf(self._krb5_conf, krb5_conf_path)
795         self._create_conf(self._kdc_conf, kdc_conf_path)
796         self._create_acl()
797         self._create_dictfile()
798
799         if create_kdb:
800             self.create_kdb()
801         if krbtgt_keysalt and create_kdb:
802             self.run([kadminl, 'cpw', '-randkey', '-e', krbtgt_keysalt,
803                       self.krbtgt_princ])
804         if create_user and create_kdb:
805             self.addprinc(self.user_princ, password('user'))
806             self.addprinc(self.admin_princ, password('admin'))
807         if create_host and create_kdb:
808             self.addprinc(self.host_princ)
809             self.extract_keytab(self.host_princ, self.keytab)
810         if start_kdc and create_kdb:
811             self.start_kdc()
812         if start_kadmind and create_kdb:
813             self.start_kadmind()
814         if get_creds and create_kdb and create_user and start_kdc:
815             self.kinit(self.user_princ, password('user'))
816             self.klist(self.user_princ)
817
818     def _create_empty_dir(self):
819         dir = self.testdir
820         shutil.rmtree(dir, True)
821         if (os.path.exists(dir)):
822             fail('Cannot remove %s to create test realm.' % dir)
823         os.mkdir(dir)
824
825     def _create_conf(self, profile, filename):
826         file = open(filename, 'w')
827         for section, contents in profile.items():
828             file.write('[%s]\n' % section)
829             self._write_cfg_section(file, contents, 1)
830         file.close()
831
832     def _write_cfg_section(self, file, contents, indent_level):
833         indent = '\t' * indent_level
834         for name, value in contents.items():
835             name = self._subst_cfg_value(name)
836             if isinstance(value, dict):
837                 # A dictionary value yields a list subsection.
838                 file.write('%s%s = {\n' % (indent, name))
839                 self._write_cfg_section(file, value, indent_level + 1)
840                 file.write('%s}\n' % indent)
841             elif isinstance(value, list):
842                 # A list value yields multiple values for the same name.
843                 for item in value:
844                     item = self._subst_cfg_value(item)
845                     file.write('%s%s = %s\n' % (indent, name, item))
846             elif isinstance(value, str):
847                 # A string value yields a straightforward variable setting.
848                 value = self._subst_cfg_value(value)
849                 file.write('%s%s = %s\n' % (indent, name, value))
850             elif value is not None:
851                 raise TypeError()
852
853     def _subst_cfg_value(self, value):
854         global buildtop, srctop, hostname
855         template = string.Template(value)
856         return template.substitute(realm=self.realm,
857                                    testdir=self.testdir,
858                                    buildtop=buildtop,
859                                    srctop=srctop,
860                                    plugins=plugins,
861                                    hostname=hostname,
862                                    port0=self.portbase,
863                                    port1=self.portbase + 1,
864                                    port2=self.portbase + 2,
865                                    port3=self.portbase + 3,
866                                    port4=self.portbase + 4,
867                                    port5=self.portbase + 5,
868                                    port6=self.portbase + 6,
869                                    port7=self.portbase + 7,
870                                    port8=self.portbase + 8,
871                                    port9=self.portbase + 9)
872
873     def _create_acl(self):
874         global hostname
875         filename = os.path.join(self.testdir, 'acl')
876         file = open(filename, 'w')
877         file.write('%s *e\n' % self.admin_princ)
878         file.write('kiprop/%s@%s p\n' % (hostname, self.realm))
879         file.close()
880
881     def _create_dictfile(self):
882         filename = os.path.join(self.testdir, 'dictfile')
883         file = open(filename, 'w')
884         file.write('weak_password\n')
885         file.close()
886
887     def _make_env(self, krb5_conf_path, kdc_conf_path):
888         env = _build_env()
889         env['KRB5_CONFIG'] = krb5_conf_path
890         env['KRB5_KDC_PROFILE'] = kdc_conf_path or os.devnull
891         env['KRB5CCNAME'] = self.ccache
892         env['KRB5_KTNAME'] = self.keytab
893         env['KRB5_CLIENT_KTNAME'] = self.client_keytab
894         env['KRB5RCACHEDIR'] = self.testdir
895         env['KPROPD_PORT'] = str(self.kprop_port())
896         env['KPROP_PORT'] = str(self.kprop_port())
897         return env
898
899     def run(self, args, env=None, **keywords):
900         if env is None:
901             env = self.env
902         return _run_cmd(args, env, **keywords)
903
904     def kprop_port(self):
905         return self.portbase + 3
906
907     def server_port(self):
908         return self.portbase + 5
909
910     def start_server(self, args, sentinel, env=None):
911         if env is None:
912             env = self.env
913         return _start_daemon(args, env, sentinel)
914
915     def start_in_inetd(self, args, port=None, env=None):
916         if not port:
917             port = self.server_port()
918         if env is None:
919             env = self.env
920         inetd_args = [t_inetd, str(port)] + args
921         return _start_daemon(inetd_args, env, 'Ready!')
922
923     def create_kdb(self):
924         global kdb5_util
925         self.run([kdb5_util, 'create', '-W', '-s', '-P', 'master'])
926
927     def start_kdc(self, args=[], env=None):
928         global krb5kdc
929         if env is None:
930             env = self.env
931         assert(self._kdc_proc is None)
932         self._kdc_proc = _start_daemon([krb5kdc, '-n'] + args, env,
933                                        'starting...')
934
935     def stop_kdc(self):
936         assert(self._kdc_proc is not None)
937         stop_daemon(self._kdc_proc)
938         self._kdc_proc = None
939
940     def start_kadmind(self, env=None):
941         global krb5kdc
942         if env is None:
943             env = self.env
944         assert(self._kadmind_proc is None)
945         dump_path = os.path.join(self.testdir, 'dump')
946         self._kadmind_proc = _start_daemon([kadmind, '-nofork', '-W',
947                                             '-p', kdb5_util, '-K', kprop,
948                                             '-F', dump_path], env,
949                                            'starting...')
950
951     def stop_kadmind(self):
952         assert(self._kadmind_proc is not None)
953         stop_daemon(self._kadmind_proc)
954         self._kadmind_proc = None
955
956     def _kpropd_args(self):
957         slavedump_path = os.path.join(self.testdir, 'incoming-slave-datatrans')
958         kpropdacl_path = os.path.join(self.testdir, 'kpropd-acl')
959         return [kpropd, '-D', '-P', str(self.kprop_port()),
960                 '-f', slavedump_path, '-p', kdb5_util, '-a', kpropdacl_path]
961
962     def start_kpropd(self, env, args=[]):
963         proc = _start_daemon(self._kpropd_args() + args, env, 'ready')
964         self._kpropd_procs.append(proc)
965         return proc
966
967     def run_kpropd_once(self, env, args=[]):
968         return self.run(self._kpropd_args() + ['-t'] + args, env=env)
969
970     def stop(self):
971         if self._kdc_proc:
972             self.stop_kdc()
973         if self._kadmind_proc:
974             self.stop_kadmind()
975         for p in self._kpropd_procs:
976             stop_daemon(p)
977         self._kpropd_procs = []
978
979     def addprinc(self, princname, password=None):
980         if password:
981             self.run([kadminl, 'addprinc', '-pw', password, princname])
982         else:
983             self.run([kadminl, 'addprinc', '-randkey', princname])
984
985     def extract_keytab(self, princname, keytab):
986         self.run([kadminl, 'ktadd', '-k', keytab, '-norandkey', princname])
987
988     def kinit(self, princname, password=None, flags=[], **keywords):
989         if password:
990             input = password + "\n"
991         else:
992             input = None
993         return self.run([kinit] + flags + [princname], input=input, **keywords)
994
995     def klist(self, client_princ, service_princ=None, ccache=None, **keywords):
996         if service_princ is None:
997             service_princ = self.krbtgt_princ
998         if ccache is None:
999             ccache = self.ccache
1000         ccachestr = ccache
1001         if len(ccachestr) < 2 or ':' not in ccachestr[2:]:
1002             ccachestr = 'FILE:' + ccachestr
1003         output = self.run([klist, ccache], **keywords)
1004         if (('Ticket cache: %s\n' % ccachestr) not in output or
1005             ('Default principal: %s\n' % client_princ) not in output or
1006             service_princ not in output):
1007             fail('Unexpected klist output.')
1008
1009     def klist_keytab(self, princ, keytab=None, **keywords):
1010         if keytab is None:
1011             keytab = self.keytab
1012         output = self.run([klist, '-k', keytab], **keywords)
1013         if (('Keytab name: FILE:%s\n' % keytab) not in output or
1014             'KVNO Principal\n----' not in output or
1015             princ not in output):
1016             fail('Unexpected klist output.')
1017
1018     def prep_kadmin(self, princname=None, pw=None, flags=[]):
1019         if princname is None:
1020             princname = self.admin_princ
1021             pw = password('admin')
1022         return self.kinit(princname, pw,
1023                           flags=['-S', 'kadmin/admin',
1024                                  '-c', self.kadmin_ccache] + flags)
1025
1026     def run_kadmin(self, args, **keywords):
1027         return self.run([kadmin, '-c', self.kadmin_ccache] + args, **keywords)
1028
1029     def special_env(self, name, has_kdc_conf, krb5_conf=None, kdc_conf=None):
1030         krb5_conf_path = os.path.join(self.testdir, 'krb5.conf.%s' % name)
1031         krb5_conf = _cfg_merge(self._krb5_conf, krb5_conf)
1032         self._create_conf(krb5_conf, krb5_conf_path)
1033         if has_kdc_conf:
1034             kdc_conf_path = os.path.join(self.testdir, 'kdc.conf.%s' % name)
1035             kdc_conf = _cfg_merge(self._kdc_conf, kdc_conf)
1036             self._create_conf(kdc_conf, kdc_conf_path)
1037         else:
1038             kdc_conf_path = None
1039         return self._make_env(krb5_conf_path, kdc_conf_path)
1040
1041
1042 def multipass_realms(**keywords):
1043     global _current_pass, _passes, testpass
1044     caller_krb5_conf = keywords.get('krb5_conf')
1045     caller_kdc_conf = keywords.get('kdc_conf')
1046     for p in _passes:
1047         (name, krbtgt_keysalt, krb5_conf, kdc_conf) = p
1048         if testpass and name != testpass:
1049             continue
1050         output('*** Beginning pass %s\n' % name)
1051         keywords['krb5_conf'] = _cfg_merge(krb5_conf, caller_krb5_conf)
1052         keywords['kdc_conf'] = _cfg_merge(kdc_conf, caller_kdc_conf)
1053         keywords['krbtgt_keysalt'] = krbtgt_keysalt
1054         _current_pass = name
1055         realm = K5Realm(**keywords)
1056         yield realm
1057         realm.stop()
1058         _current_pass = None
1059
1060
1061 def cross_realms(num, xtgts=None, args=None, **keywords):
1062     # Build keyword args for each realm.
1063     realm_args = []
1064     for i in range(num):
1065         realmnumber = i + 1
1066         # Start with any global keyword arguments to this function.
1067         a = keywords.copy()
1068         if args and args[i]:
1069             # Merge in specific arguments for this realm.  Use
1070             # _cfg_merge for config fragments.
1071             a.update(args[i])
1072             for cf in ('krb5_conf', 'kdc_conf'):
1073                 if cf in keywords and cf in args[i]:
1074                     a[cf] = _cfg_merge(keywords[cf], args[i][cf])
1075         # Set defaults for the realm name, testdir, and portbase.
1076         if not 'realm' in a:
1077             a['realm'] = 'KRBTEST%d.COM' % realmnumber
1078         if not 'testdir' in a:
1079             a['testdir'] = os.path.join('testdir', str(realmnumber))
1080         if not 'portbase' in a:
1081             a['portbase'] = 61000 + 10 * realmnumber
1082         realm_args.append(a)
1083         
1084     # Build a [realms] config fragment containing all of the realms.
1085     realmsection = { '$realm' : None }
1086     for a in realm_args:
1087         name = a['realm']
1088         portbase = a['portbase']
1089         realmsection[name] = {
1090             'kdc' : '$hostname:%d' % portbase,
1091             'admin_server' : '$hostname:%d' % (portbase + 1),
1092             'kpasswd_server' : '$hostname:%d' % (portbase + 2)
1093             }
1094     realmscfg = {'realms': realmsection}
1095
1096     # Set realmsection in each realm's krb5_conf keyword argument.
1097     for a in realm_args:
1098         a['krb5_conf'] = _cfg_merge(realmscfg, a.get('krb5_conf'))
1099
1100     if xtgts is None:
1101         # Default to cross tgts for every pair of realms.
1102         # (itertools.permutations would work here but is new in 2.6.)
1103         xtgts = [(x,y) for x in range(num) for y in range(num) if x != y]
1104
1105     # Create the realms.
1106     realms = []
1107     for i in range(num):
1108         r = K5Realm(**realm_args[i])
1109         # Create specified cross TGTs in this realm's db.
1110         for j in range(num):
1111             if j == i:
1112                 continue
1113             iname = r.realm
1114             jname = realm_args[j]['realm']
1115             if (i, j) in xtgts:
1116                 # This realm can authenticate to realm j.
1117                 r.addprinc('krbtgt/%s' % jname, password('cr-%d-%d-' % (i, j)))
1118             if (j, i) in xtgts:
1119                 # Realm j can authenticate to this realm.
1120                 r.addprinc('krbtgt/%s@%s' % (iname, jname),
1121                            password('cr-%d-%d-' % (j, i)))
1122         realms.append(r)
1123     return realms
1124
1125
1126 _default_krb5_conf = {
1127     'libdefaults': {
1128         'default_realm': '$realm',
1129         'dns_lookup_kdc': 'false',
1130         'plugin_base_dir': '$plugins'},
1131     'realms': {'$realm': {
1132             'kdc': '$hostname:$port0',
1133             'admin_server': '$hostname:$port1',
1134             'kpasswd_server': '$hostname:$port2'}}}
1135
1136
1137 _default_kdc_conf = {
1138     'realms': {'$realm': {
1139             'database_module': 'db',
1140             'iprop_port': '$port4',
1141             'key_stash_file': '$testdir/stash',
1142             'acl_file': '$testdir/acl',
1143             'dictfile': '$testdir/dictfile',
1144             'kadmind_port': '$port1',
1145             'kpasswd_port': '$port2',
1146             'kdc_listen': '$port0',
1147             'kdc_tcp_listen': '$port0'}},
1148     'dbmodules': {
1149         'db_module_dir': '$plugins/kdb',
1150         'db': {'db_library': 'db2', 'database_name' : '$testdir/db'}},
1151     'logging': {
1152         'admin_server': 'FILE:$testdir/kadmind5.log',
1153         'kdc': 'FILE:$testdir/kdc.log',
1154         'default': 'FILE:$testdir/others.log'}}
1155
1156
1157 # A pass is a tuple of: name, krbtgt_keysalt, krb5_conf, kdc_conf.
1158 _passes = [
1159     # No special settings; exercises AES256.
1160     ('default', None, None, None),
1161
1162     # Exercise a DES enctype and the v4 salt type.
1163     ('desv4', None,
1164      {'libdefaults': {
1165                 'default_tgs_enctypes': 'des-cbc-crc',
1166                 'default_tkt_enctypes': 'des-cbc-crc',
1167                 'permitted_enctypes': 'des-cbc-crc',
1168                 'allow_weak_crypto': 'true'}},
1169      {'realms': {'$realm': {
1170                     'supported_enctypes': 'des-cbc-crc:v4',
1171                     'master_key_type': 'des-cbc-crc'}}}),
1172
1173     # Exercise the DES3 enctype.
1174     ('des3', None,
1175      {'libdefaults': {
1176                 'default_tgs_enctypes': 'des3',
1177                 'default_tkt_enctypes': 'des3',
1178                 'permitted_enctypes': 'des3'}},
1179      {'realms': {'$realm': {
1180                     'supported_enctypes': 'des3-cbc-sha1:normal',
1181                     'master_key_type': 'des3-cbc-sha1'}}}),
1182
1183     # Exercise the arcfour enctype.
1184     ('arcfour', None,
1185      {'libdefaults': {
1186                 'default_tgs_enctypes': 'rc4',
1187                 'default_tkt_enctypes': 'rc4',
1188                 'permitted_enctypes': 'rc4'}},
1189      {'realms': {'$realm': {
1190                     'supported_enctypes': 'arcfour-hmac:normal',
1191                     'master_key_type': 'arcfour-hmac'}}}),
1192
1193     # Exercise the AES128 enctype.
1194     ('aes128', None,
1195       {'libdefaults': {
1196                 'default_tgs_enctypes': 'aes128-cts',
1197                 'default_tkt_enctypes': 'aes128-cts',
1198                 'permitted_enctypes': 'aes128-cts'}},
1199       {'realms': {'$realm': {
1200                     'supported_enctypes': 'aes128-cts:normal',
1201                     'master_key_type': 'aes128-cts'}}}),
1202
1203     # Exercise the camellia256-cts enctype.
1204     ('camellia256', None,
1205       {'libdefaults': {
1206                 'default_tgs_enctypes': 'camellia256-cts',
1207                 'default_tkt_enctypes': 'camellia256-cts',
1208                 'permitted_enctypes': 'camellia256-cts'}},
1209       {'realms': {'$realm': {
1210                     'supported_enctypes': 'camellia256-cts:normal',
1211                     'master_key_type': 'camellia256-cts'}}}),
1212
1213     # Exercise the aes128-sha2 enctype.
1214     ('aes128-sha2', None,
1215       {'libdefaults': {
1216                 'default_tgs_enctypes': 'aes128-sha2',
1217                 'default_tkt_enctypes': 'aes128-sha2',
1218                 'permitted_enctypes': 'aes128-sha2'}},
1219       {'realms': {'$realm': {
1220                     'supported_enctypes': 'aes128-sha2:normal',
1221                     'master_key_type': 'aes128-sha2'}}}),
1222
1223     # Exercise the aes256-sha2 enctype.
1224     ('aes256-sha2', None,
1225       {'libdefaults': {
1226                 'default_tgs_enctypes': 'aes256-sha2',
1227                 'default_tkt_enctypes': 'aes256-sha2',
1228                 'permitted_enctypes': 'aes256-sha2'}},
1229       {'realms': {'$realm': {
1230                     'supported_enctypes': 'aes256-sha2:normal',
1231                     'master_key_type': 'aes256-sha2'}}}),
1232
1233     # Test a setup with modern principal keys but an old TGT key.
1234     ('aes256.destgt', 'des-cbc-crc:normal',
1235      {'libdefaults': {'allow_weak_crypto': 'true'}},
1236      None)
1237 ]
1238
1239 _success = False
1240 _current_pass = None
1241 _daemons = []
1242 _parse_args()
1243 atexit.register(_onexit)
1244 signal.signal(signal.SIGINT, _onsigint)
1245 _outfile = open('testlog', 'w')
1246 _cmd_index = 1
1247 _last_cmd = None
1248 _last_cmd_output = None
1249 buildtop = _find_buildtop()
1250 srctop = _find_srctop()
1251 plugins = os.path.join(buildtop, 'plugins')
1252 runenv = _import_runenv()
1253 hostname = _get_hostname()
1254 null_input = open(os.devnull, 'r')
1255
1256 krb5kdc = os.path.join(buildtop, 'kdc', 'krb5kdc')
1257 kadmind = os.path.join(buildtop, 'kadmin', 'server', 'kadmind')
1258 kadmin = os.path.join(buildtop, 'kadmin', 'cli', 'kadmin')
1259 kadminl = os.path.join(buildtop, 'kadmin', 'cli', 'kadmin.local')
1260 kdb5_ldap_util = os.path.join(buildtop, 'plugins', 'kdb', 'ldap', 'ldap_util',
1261                               'kdb5_ldap_util')
1262 kdb5_util = os.path.join(buildtop, 'kadmin', 'dbutil', 'kdb5_util')
1263 ktutil = os.path.join(buildtop, 'kadmin', 'ktutil', 'ktutil')
1264 kinit = os.path.join(buildtop, 'clients', 'kinit', 'kinit')
1265 klist = os.path.join(buildtop, 'clients', 'klist', 'klist')
1266 kswitch = os.path.join(buildtop, 'clients', 'kswitch', 'kswitch')
1267 kvno = os.path.join(buildtop, 'clients', 'kvno', 'kvno')
1268 kdestroy = os.path.join(buildtop, 'clients', 'kdestroy', 'kdestroy')
1269 kpasswd = os.path.join(buildtop, 'clients', 'kpasswd', 'kpasswd')
1270 t_inetd = os.path.join(buildtop, 'tests', 'dejagnu', 't_inetd')
1271 kproplog = os.path.join(buildtop, 'slave', 'kproplog')
1272 kpropd = os.path.join(buildtop, 'slave', 'kpropd')
1273 kprop = os.path.join(buildtop, 'slave', 'kprop')