1*0d9d0fd8Schristos /* $NetBSD: pamtest.c,v 1.8 2023/06/30 21:46:20 christos Exp $ */
244269bb5Schristos
344269bb5Schristos /*-
444269bb5Schristos * Copyright (c) 2011 Dag-Erling Smørgrav
544269bb5Schristos * All rights reserved.
644269bb5Schristos *
744269bb5Schristos * Redistribution and use in source and binary forms, with or without
844269bb5Schristos * modification, are permitted provided that the following conditions
944269bb5Schristos * are met:
1044269bb5Schristos * 1. Redistributions of source code must retain the above copyright
118fa0fefeSchristos * notice, this list of conditions and the following disclaimer.
1244269bb5Schristos * 2. Redistributions in binary form must reproduce the above copyright
1344269bb5Schristos * notice, this list of conditions and the following disclaimer in the
1444269bb5Schristos * documentation and/or other materials provided with the distribution.
15e1b25b17Schristos * 3. The name of the author may not be used to endorse or promote
16e1b25b17Schristos * products derived from this software without specific prior written
17e1b25b17Schristos * permission.
1844269bb5Schristos *
1944269bb5Schristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2044269bb5Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2144269bb5Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2244269bb5Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2344269bb5Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2444269bb5Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2544269bb5Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2644269bb5Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2744269bb5Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2844269bb5Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2944269bb5Schristos * SUCH DAMAGE.
3044269bb5Schristos */
3144269bb5Schristos
3244269bb5Schristos #ifdef HAVE_CONFIG_H
3344269bb5Schristos # include "config.h"
3444269bb5Schristos #endif
3544269bb5Schristos
3644269bb5Schristos #include <err.h>
378fa0fefeSchristos #include <limits.h>
3844269bb5Schristos #include <pwd.h>
3944269bb5Schristos #include <stdarg.h>
4044269bb5Schristos #include <stdio.h>
4144269bb5Schristos #include <stdlib.h>
4244269bb5Schristos #include <string.h>
4344269bb5Schristos #include <unistd.h>
4444269bb5Schristos
4544269bb5Schristos #include <security/pam_appl.h>
4644269bb5Schristos #include <security/openpam.h> /* for openpam_ttyconv() */
4744269bb5Schristos
4844269bb5Schristos /* OpenPAM internals */
4944269bb5Schristos extern const char *pam_item_name[PAM_NUM_ITEMS];
5044269bb5Schristos extern int openpam_debug;
5144269bb5Schristos
5244269bb5Schristos static pam_handle_t *pamh;
5344269bb5Schristos static struct pam_conv pamc;
5444269bb5Schristos
5544269bb5Schristos static int silent;
5644269bb5Schristos static int verbose;
5744269bb5Schristos
5844269bb5Schristos static void pt_verbose(const char *, ...)
5944269bb5Schristos OPENPAM_FORMAT ((__printf__, 1, 2));
6044269bb5Schristos static void pt_error(int, const char *, ...)
6144269bb5Schristos OPENPAM_FORMAT ((__printf__, 2, 3));
6244269bb5Schristos
6344269bb5Schristos /*
6444269bb5Schristos * Print an information message if -v was specified at least once
6544269bb5Schristos */
6644269bb5Schristos static void
pt_verbose(const char * fmt,...)6744269bb5Schristos pt_verbose(const char *fmt, ...)
6844269bb5Schristos {
6944269bb5Schristos va_list ap;
7044269bb5Schristos
7144269bb5Schristos if (verbose) {
7244269bb5Schristos va_start(ap, fmt);
7344269bb5Schristos vfprintf(stderr, fmt, ap);
7444269bb5Schristos va_end(ap);
7544269bb5Schristos fprintf(stderr, "\n");
7644269bb5Schristos }
7744269bb5Schristos }
7844269bb5Schristos
7944269bb5Schristos /*
8044269bb5Schristos * Print an error message
8144269bb5Schristos */
8244269bb5Schristos static void
pt_error(int e,const char * fmt,...)8344269bb5Schristos pt_error(int e, const char *fmt, ...)
8444269bb5Schristos {
8544269bb5Schristos va_list ap;
8644269bb5Schristos
8744269bb5Schristos if (e == PAM_SUCCESS && !verbose)
8844269bb5Schristos return;
8944269bb5Schristos va_start(ap, fmt);
9044269bb5Schristos vfprintf(stderr, fmt, ap);
9144269bb5Schristos va_end(ap);
9244269bb5Schristos fprintf(stderr, ": %s\n", pam_strerror(NULL, e));
9344269bb5Schristos }
9444269bb5Schristos
9544269bb5Schristos /*
9644269bb5Schristos * Wrapper for pam_start(3)
9744269bb5Schristos */
9844269bb5Schristos static int
pt_start(const char * service,const char * user)9944269bb5Schristos pt_start(const char *service, const char *user)
10044269bb5Schristos {
10144269bb5Schristos int pame;
10244269bb5Schristos
10344269bb5Schristos pamc.conv = &openpam_ttyconv;
10444269bb5Schristos pt_verbose("pam_start(%s, %s)", service, user);
10544269bb5Schristos if ((pame = pam_start(service, user, &pamc, &pamh)) != PAM_SUCCESS)
10644269bb5Schristos pt_error(pame, "pam_start(%s)", service);
10744269bb5Schristos return (pame);
10844269bb5Schristos }
10944269bb5Schristos
11044269bb5Schristos /*
11144269bb5Schristos * Wrapper for pam_authenticate(3)
11244269bb5Schristos */
11344269bb5Schristos static int
pt_authenticate(int flags)11444269bb5Schristos pt_authenticate(int flags)
11544269bb5Schristos {
11644269bb5Schristos int pame;
11744269bb5Schristos
11844269bb5Schristos flags |= silent;
1198fa0fefeSchristos pt_verbose("pam_authenticate()");
12044269bb5Schristos if ((pame = pam_authenticate(pamh, flags)) != PAM_SUCCESS)
12144269bb5Schristos pt_error(pame, "pam_authenticate()");
12244269bb5Schristos return (pame);
12344269bb5Schristos }
12444269bb5Schristos
12544269bb5Schristos /*
12644269bb5Schristos * Wrapper for pam_acct_mgmt(3)
12744269bb5Schristos */
12844269bb5Schristos static int
pt_acct_mgmt(int flags)12944269bb5Schristos pt_acct_mgmt(int flags)
13044269bb5Schristos {
13144269bb5Schristos int pame;
13244269bb5Schristos
13344269bb5Schristos flags |= silent;
1348fa0fefeSchristos pt_verbose("pam_acct_mgmt()");
13544269bb5Schristos if ((pame = pam_acct_mgmt(pamh, flags)) != PAM_SUCCESS)
13644269bb5Schristos pt_error(pame, "pam_acct_mgmt()");
13744269bb5Schristos return (pame);
13844269bb5Schristos }
13944269bb5Schristos
14044269bb5Schristos /*
14144269bb5Schristos * Wrapper for pam_chauthtok(3)
14244269bb5Schristos */
14344269bb5Schristos static int
pt_chauthtok(int flags)14444269bb5Schristos pt_chauthtok(int flags)
14544269bb5Schristos {
14644269bb5Schristos int pame;
14744269bb5Schristos
14844269bb5Schristos flags |= silent;
1498fa0fefeSchristos pt_verbose("pam_chauthtok()");
15044269bb5Schristos if ((pame = pam_chauthtok(pamh, flags)) != PAM_SUCCESS)
15144269bb5Schristos pt_error(pame, "pam_chauthtok()");
15244269bb5Schristos return (pame);
15344269bb5Schristos }
15444269bb5Schristos
15544269bb5Schristos /*
15644269bb5Schristos * Wrapper for pam_setcred(3)
15744269bb5Schristos */
15844269bb5Schristos static int
pt_setcred(int flags)15944269bb5Schristos pt_setcred(int flags)
16044269bb5Schristos {
16144269bb5Schristos int pame;
16244269bb5Schristos
16344269bb5Schristos flags |= silent;
1648fa0fefeSchristos pt_verbose("pam_setcred()");
16544269bb5Schristos if ((pame = pam_setcred(pamh, flags)) != PAM_SUCCESS)
16644269bb5Schristos pt_error(pame, "pam_setcred()");
16744269bb5Schristos return (pame);
16844269bb5Schristos }
16944269bb5Schristos
17044269bb5Schristos /*
17144269bb5Schristos * Wrapper for pam_open_session(3)
17244269bb5Schristos */
17344269bb5Schristos static int
pt_open_session(int flags)17444269bb5Schristos pt_open_session(int flags)
17544269bb5Schristos {
17644269bb5Schristos int pame;
17744269bb5Schristos
17844269bb5Schristos flags |= silent;
1798fa0fefeSchristos pt_verbose("pam_open_session()");
18044269bb5Schristos if ((pame = pam_open_session(pamh, flags)) != PAM_SUCCESS)
18144269bb5Schristos pt_error(pame, "pam_open_session()");
18244269bb5Schristos return (pame);
18344269bb5Schristos }
18444269bb5Schristos
18544269bb5Schristos /*
18644269bb5Schristos * Wrapper for pam_close_session(3)
18744269bb5Schristos */
18844269bb5Schristos static int
pt_close_session(int flags)18944269bb5Schristos pt_close_session(int flags)
19044269bb5Schristos {
19144269bb5Schristos int pame;
19244269bb5Schristos
19344269bb5Schristos flags |= silent;
1948fa0fefeSchristos pt_verbose("pam_close_session()");
19544269bb5Schristos if ((pame = pam_close_session(pamh, flags)) != PAM_SUCCESS)
19644269bb5Schristos pt_error(pame, "pam_close_session()");
19744269bb5Schristos return (pame);
19844269bb5Schristos }
19944269bb5Schristos
20044269bb5Schristos /*
20144269bb5Schristos * Wrapper for pam_set_item(3)
20244269bb5Schristos */
20344269bb5Schristos static int
pt_set_item(int item,const char * p)20444269bb5Schristos pt_set_item(int item, const char *p)
20544269bb5Schristos {
20644269bb5Schristos int pame;
20744269bb5Schristos
20844269bb5Schristos switch (item) {
20944269bb5Schristos case PAM_SERVICE:
21044269bb5Schristos case PAM_USER:
21144269bb5Schristos case PAM_AUTHTOK:
21244269bb5Schristos case PAM_OLDAUTHTOK:
21344269bb5Schristos case PAM_TTY:
21444269bb5Schristos case PAM_RHOST:
21544269bb5Schristos case PAM_RUSER:
21644269bb5Schristos case PAM_USER_PROMPT:
21744269bb5Schristos case PAM_AUTHTOK_PROMPT:
21844269bb5Schristos case PAM_OLDAUTHTOK_PROMPT:
21944269bb5Schristos case PAM_HOST:
22044269bb5Schristos pt_verbose("setting %s to %s", pam_item_name[item], p);
22144269bb5Schristos break;
22244269bb5Schristos default:
22344269bb5Schristos pt_verbose("setting %s", pam_item_name[item]);
22444269bb5Schristos break;
22544269bb5Schristos }
22644269bb5Schristos if ((pame = pam_set_item(pamh, item, p)) != PAM_SUCCESS)
22744269bb5Schristos pt_error(pame, "pam_set_item(%s)", pam_item_name[item]);
22844269bb5Schristos return (pame);
22944269bb5Schristos }
23044269bb5Schristos
23144269bb5Schristos /*
23244269bb5Schristos * Wrapper for pam_end(3)
23344269bb5Schristos */
23444269bb5Schristos static int
pt_end(int pame)23544269bb5Schristos pt_end(int pame)
23644269bb5Schristos {
23744269bb5Schristos
23844269bb5Schristos if (pamh != NULL && (pame = pam_end(pamh, pame)) != PAM_SUCCESS)
23944269bb5Schristos /* can't happen */
24044269bb5Schristos pt_error(pame, "pam_end()");
24144269bb5Schristos return (pame);
24244269bb5Schristos }
24344269bb5Schristos
24444269bb5Schristos /*
24544269bb5Schristos * Retrieve and list the PAM environment variables
24644269bb5Schristos */
24744269bb5Schristos static int
pt_listenv(void)24844269bb5Schristos pt_listenv(void)
24944269bb5Schristos {
25044269bb5Schristos char **pam_envlist, **pam_env;
25144269bb5Schristos
25244269bb5Schristos if ((pam_envlist = pam_getenvlist(pamh)) == NULL ||
25344269bb5Schristos *pam_envlist == NULL) {
25444269bb5Schristos pt_verbose("no environment variables.");
25544269bb5Schristos } else {
25644269bb5Schristos pt_verbose("environment variables:");
25744269bb5Schristos for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
25844269bb5Schristos printf(" %s\n", *pam_env);
25944269bb5Schristos free(*pam_env);
26044269bb5Schristos }
26144269bb5Schristos }
26244269bb5Schristos free(pam_envlist);
26344269bb5Schristos return (PAM_SUCCESS);
26444269bb5Schristos }
26544269bb5Schristos
26644269bb5Schristos /*
26744269bb5Schristos * Print usage string and exit
26844269bb5Schristos */
26944269bb5Schristos static void
usage(void)27044269bb5Schristos usage(void)
27144269bb5Schristos {
27244269bb5Schristos
273e1b25b17Schristos fprintf(stderr, "usage: pamtest %s service command ...\n",
274e1b25b17Schristos "[-dkMPsv] [-H rhost] [-h host] [-t tty] [-U ruser] [-u user]");
27544269bb5Schristos exit(1);
27644269bb5Schristos }
27744269bb5Schristos
27844269bb5Schristos /*
2798fa0fefeSchristos * Handle an option that takes an int argument and can be used only once
2808fa0fefeSchristos */
2818fa0fefeSchristos static void
opt_num_once(int opt,long * num,const char * arg)2828fa0fefeSchristos opt_num_once(int opt, long *num, const char *arg)
2838fa0fefeSchristos {
2848fa0fefeSchristos char *end;
2858fa0fefeSchristos long l;
2868fa0fefeSchristos
2878fa0fefeSchristos l = strtol(arg, &end, 0);
2888fa0fefeSchristos if (end == optarg || *end != '\0') {
2898fa0fefeSchristos fprintf(stderr,
2908fa0fefeSchristos "The -%c option expects a numeric argument\n", opt);
2918fa0fefeSchristos usage();
2928fa0fefeSchristos }
2938fa0fefeSchristos *num = l;
2948fa0fefeSchristos }
2958fa0fefeSchristos
2968fa0fefeSchristos /*
29744269bb5Schristos * Handle an option that takes a string argument and can be used only once
29844269bb5Schristos */
29944269bb5Schristos static void
opt_str_once(int opt,const char ** p,const char * arg)30044269bb5Schristos opt_str_once(int opt, const char **p, const char *arg)
30144269bb5Schristos {
30244269bb5Schristos
30344269bb5Schristos if (*p != NULL) {
30444269bb5Schristos fprintf(stderr, "The -%c option can only be used once\n", opt);
30544269bb5Schristos usage();
30644269bb5Schristos }
30744269bb5Schristos *p = arg;
30844269bb5Schristos }
30944269bb5Schristos
31044269bb5Schristos /*
31144269bb5Schristos * Entry point
31244269bb5Schristos */
31344269bb5Schristos int
main(int argc,char * argv[])31444269bb5Schristos main(int argc, char *argv[])
31544269bb5Schristos {
31644269bb5Schristos char hostname[1024];
31744269bb5Schristos const char *rhost = NULL;
31844269bb5Schristos const char *host = NULL;
31944269bb5Schristos const char *ruser = NULL;
32044269bb5Schristos const char *user = NULL;
32144269bb5Schristos const char *service = NULL;
32244269bb5Schristos const char *tty = NULL;
3238fa0fefeSchristos long timeout = 0;
32444269bb5Schristos int keepatit = 0;
32544269bb5Schristos int pame;
32644269bb5Schristos int opt;
32744269bb5Schristos
3288fa0fefeSchristos while ((opt = getopt(argc, argv, "dH:h:kMPsT:t:U:u:v")) != -1)
32944269bb5Schristos switch (opt) {
33044269bb5Schristos case 'd':
33144269bb5Schristos openpam_debug++;
33244269bb5Schristos break;
33344269bb5Schristos case 'H':
33444269bb5Schristos opt_str_once(opt, &rhost, optarg);
33544269bb5Schristos break;
33644269bb5Schristos case 'h':
33744269bb5Schristos opt_str_once(opt, &host, optarg);
33844269bb5Schristos break;
33944269bb5Schristos case 'k':
34044269bb5Schristos keepatit = 1;
34144269bb5Schristos break;
342e1b25b17Schristos case 'M':
343e1b25b17Schristos openpam_set_feature(OPENPAM_RESTRICT_MODULE_NAME, 0);
344e1b25b17Schristos openpam_set_feature(OPENPAM_VERIFY_MODULE_FILE, 0);
345e1b25b17Schristos break;
346e1b25b17Schristos case 'P':
347e1b25b17Schristos openpam_set_feature(OPENPAM_RESTRICT_SERVICE_NAME, 0);
348e1b25b17Schristos openpam_set_feature(OPENPAM_VERIFY_POLICY_FILE, 0);
349e1b25b17Schristos break;
35044269bb5Schristos case 's':
35144269bb5Schristos silent = PAM_SILENT;
35244269bb5Schristos break;
3538fa0fefeSchristos case 'T':
3548fa0fefeSchristos opt_num_once(opt, &timeout, optarg);
3558fa0fefeSchristos if (timeout < 0 || timeout > INT_MAX) {
3568fa0fefeSchristos fprintf(stderr,
3578fa0fefeSchristos "Invalid conversation timeout\n");
3588fa0fefeSchristos usage();
3598fa0fefeSchristos }
3608fa0fefeSchristos openpam_ttyconv_timeout = (int)timeout;
3618fa0fefeSchristos break;
36244269bb5Schristos case 't':
36344269bb5Schristos opt_str_once(opt, &tty, optarg);
36444269bb5Schristos break;
36544269bb5Schristos case 'U':
36644269bb5Schristos opt_str_once(opt, &ruser, optarg);
36744269bb5Schristos break;
36844269bb5Schristos case 'u':
36944269bb5Schristos opt_str_once(opt, &user, optarg);
37044269bb5Schristos break;
37144269bb5Schristos case 'v':
37244269bb5Schristos verbose++;
37344269bb5Schristos break;
37444269bb5Schristos default:
37544269bb5Schristos usage();
37644269bb5Schristos }
37744269bb5Schristos
37844269bb5Schristos argc -= optind;
37944269bb5Schristos argv += optind;
38044269bb5Schristos
38144269bb5Schristos if (argc < 1)
38244269bb5Schristos usage();
38344269bb5Schristos
38444269bb5Schristos service = *argv;
38544269bb5Schristos --argc;
38644269bb5Schristos ++argv;
38744269bb5Schristos
38844269bb5Schristos /* defaults */
3898fa0fefeSchristos if (service == NULL)
3908fa0fefeSchristos service = "pamtest";
39144269bb5Schristos if (rhost == NULL) {
39244269bb5Schristos if (gethostname(hostname, sizeof(hostname)) == -1)
39344269bb5Schristos err(1, "gethostname()");
39444269bb5Schristos rhost = hostname;
39544269bb5Schristos }
39644269bb5Schristos if (tty == NULL)
39744269bb5Schristos tty = ttyname(STDERR_FILENO);
39844269bb5Schristos if (user == NULL)
39944269bb5Schristos user = getlogin();
40044269bb5Schristos if (ruser == NULL)
40144269bb5Schristos ruser = user;
40244269bb5Schristos
40344269bb5Schristos /* initialize PAM */
40444269bb5Schristos if ((pame = pt_start(service, user)) != PAM_SUCCESS)
40544269bb5Schristos goto end;
40644269bb5Schristos
40744269bb5Schristos /*
40844269bb5Schristos * pam_start(3) sets this to the machine's hostname, but we allow
40944269bb5Schristos * the user to override it.
41044269bb5Schristos */
41144269bb5Schristos if (host != NULL)
41244269bb5Schristos if ((pame = pt_set_item(PAM_HOST, host)) != PAM_SUCCESS)
41344269bb5Schristos goto end;
41444269bb5Schristos
41544269bb5Schristos /*
41644269bb5Schristos * The remote host / user / tty are usually set by the
41744269bb5Schristos * application.
41844269bb5Schristos */
41944269bb5Schristos if ((pame = pt_set_item(PAM_RHOST, rhost)) != PAM_SUCCESS ||
42044269bb5Schristos (pame = pt_set_item(PAM_RUSER, ruser)) != PAM_SUCCESS ||
42144269bb5Schristos (pame = pt_set_item(PAM_TTY, tty)) != PAM_SUCCESS)
42244269bb5Schristos goto end;
42344269bb5Schristos
42444269bb5Schristos while (argc > 0) {
42544269bb5Schristos if (strcmp(*argv, "listenv") == 0 ||
42644269bb5Schristos strcmp(*argv, "env") == 0) {
42744269bb5Schristos pame = pt_listenv();
42844269bb5Schristos } else if (strcmp(*argv, "authenticate") == 0 ||
42944269bb5Schristos strcmp(*argv, "auth") == 0) {
43044269bb5Schristos pame = pt_authenticate(0);
43144269bb5Schristos } else if (strcmp(*argv, "acct_mgmt") == 0 ||
43244269bb5Schristos strcmp(*argv, "account") == 0) {
43344269bb5Schristos pame = pt_acct_mgmt(0);
43444269bb5Schristos } else if (strcmp(*argv, "chauthtok") == 0 ||
43544269bb5Schristos strcmp(*argv, "change") == 0) {
43644269bb5Schristos pame = pt_chauthtok(PAM_CHANGE_EXPIRED_AUTHTOK);
43744269bb5Schristos } else if (strcmp(*argv, "forcechauthtok") == 0 ||
43844269bb5Schristos strcmp(*argv, "forcechange") == 0) {
43944269bb5Schristos pame = pt_chauthtok(0);
44044269bb5Schristos } else if (strcmp(*argv, "setcred") == 0 ||
44144269bb5Schristos strcmp(*argv, "establish_cred") == 0) {
44244269bb5Schristos pame = pt_setcred(PAM_ESTABLISH_CRED);
44344269bb5Schristos } else if (strcmp(*argv, "open_session") == 0 ||
44444269bb5Schristos strcmp(*argv, "open") == 0) {
44544269bb5Schristos pame = pt_open_session(0);
44644269bb5Schristos } else if (strcmp(*argv, "close_session") == 0 ||
44744269bb5Schristos strcmp(*argv, "close") == 0) {
44844269bb5Schristos pame = pt_close_session(0);
44944269bb5Schristos } else if (strcmp(*argv, "unsetcred") == 0 ||
45044269bb5Schristos strcmp(*argv, "delete_cred") == 0) {
45144269bb5Schristos pame = pt_setcred(PAM_DELETE_CRED);
45244269bb5Schristos } else {
45344269bb5Schristos warnx("unknown primitive: %s", *argv);
45444269bb5Schristos pame = PAM_SYSTEM_ERR;
45544269bb5Schristos }
45644269bb5Schristos if (pame != PAM_SUCCESS && !keepatit) {
45744269bb5Schristos warnx("test aborted");
45844269bb5Schristos break;
45944269bb5Schristos }
46044269bb5Schristos --argc;
46144269bb5Schristos ++argv;
46244269bb5Schristos }
46344269bb5Schristos
46444269bb5Schristos end:
46544269bb5Schristos (void)pt_end(pame);
46644269bb5Schristos exit(pame == PAM_SUCCESS ? 0 : 1);
46744269bb5Schristos }
468