diff options
| author | Keith Vetter <keithv@fusion.com> | 1995-09-28 00:29:20 +0000 |
|---|---|---|
| committer | Keith Vetter <keithv@fusion.com> | 1995-09-28 00:29:20 +0000 |
| commit | 9f3d80f60e0ede10438f160a1e0973b11b49a849 (patch) | |
| tree | 60f03ae38dad318a40dd487fe5dd3ca5fb73932b /src | |
| parent | 47a204486ea86d2becf3adbc82536ca0511519bb (diff) | |
Files in telnet authentication module directory
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@6877 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src')
| -rw-r--r-- | src/mac/telnet-k5-auth/ChangeLog | 4 | ||||
| -rw-r--r-- | src/mac/telnet-k5-auth/krb5auth.c | 406 | ||||
| -rw-r--r-- | src/mac/telnet-k5-auth/telnet-2.7b4-68k.sit.hqx | 2919 | ||||
| -rw-r--r-- | src/mac/telnet-k5-auth/telnet-k5-auth.sit.hqx | 272 | ||||
| -rw-r--r-- | src/mac/telnet-k5-auth/tnae.h | 142 |
5 files changed, 3743 insertions, 0 deletions
diff --git a/src/mac/telnet-k5-auth/ChangeLog b/src/mac/telnet-k5-auth/ChangeLog new file mode 100644 index 000000000..fdd62e6c4 --- /dev/null +++ b/src/mac/telnet-k5-auth/ChangeLog @@ -0,0 +1,4 @@ +Wed Sep 27 12:00:00 1995 John Rivlin <jrivlin@fusion.com> + + * Directory created with NCSA Telnet Kerberos Authentication + module project. diff --git a/src/mac/telnet-k5-auth/krb5auth.c b/src/mac/telnet-k5-auth/krb5auth.c new file mode 100644 index 000000000..81c386bf6 --- /dev/null +++ b/src/mac/telnet-k5-auth/krb5auth.c @@ -0,0 +1,406 @@ +#include "tnae.h" +#include <SetupA4.h> +/* + * The intrinsic Authorization Module, usefull for debugging a module built in to the app + * long AuthModule(long, char*); + */ + + +#ifdef KRB5 +# include "k5-int.h" +# include "com_err.h" +# include "prof_int.h" +# include "krb5.h" +#endif +#define KRB_SERVICE_NAME "host" +#define K5_REJECT 1 +#define K5_ACCEPT 2 +#define K5_RESPONSE 3 // They had to make it different +#define KSUCCESS 0 +#define KFAILURE 255 + +static krb5_context k5_context; +static krb5_auth_context *auth_context; + +long main(long func, char *parameters); +static int k5_auth_send (int how, char *szHostName, char *szUserName, krb5_data *auth); +static int k5_auth_reply (int how, unsigned char *data, int cnt); +static void tn_sendsub (tnParams *tn, int code, int request, char *scp, int length); +static void tn_sendauthsub (tnParams *tn, int code, int request, int vers, int how, int auth, char *scp, int length); + +long +main(long func, char *parameters) +{ +tnParams *tn; +char *so; +char *cp; +long err; +long oldA4; + + oldA4 = SetUpA4(); + + switch (func) { + case TNFUNC_INIT_CODE: + /* + * Initialize this code module. + * + * parameters: points to area to save type/modifier pairs + * returns: the number of pairs entered. + */ + cp = (unsigned char *)parameters; +// *cp++ = AUTH_KERBEROS_V5; +// *cp++ = AUTH_HOW_MUTUAL; /* also need AUTH_CLIENT_TO_SERVER ??? ddd */ + *cp++ = AUTH_KERBEROS_V5; + *cp++ = AUTH_HOW_ONE_WAY; + err = 2; /* 2 pairs */ + + /* initialize krb5 */ + krb5_init_context(&k5_context); + krb5_init_ets(k5_context); + break; + + case TNFUNC_INIT_SESSION_AUTH: + /* + * Initialize auth session data. + * + * parameters: pointer to where to save pointer to auth data. + */ + *parameters = (long) NewPtr(10); + + break; + +/* we don't do session encryption now */ + case TNFUNC_INIT_SESSION_ENCRYPT: + err = 0; /* we do NOT do option 38 encrypt */ + break; + case TNFUNC_ENCRYPT_SB: + err = 0; /* we do NOT do option 38 encrypt */ + break; + + case TNFUNC_DECRYPT: + err = 0; /* we do NOT do option 38 encrypt */ + break; + + case TNFUNC_ENCRYPT: + err = 0; /* we do NOT do option 38 encrypt */ + break; + + case TNFUNC_QUERY_ENCRYPT: + err = 0; /* we do NOT do option 38 encrypt */ + break; + + case TNFUNC_AUTH_SEND: + { + krb5_data auth; + char szUserName[100] = ""; + char server[100]; + /* + * Process [IAC SB] AUTH SEND <type-modifier-list> [IAC SE] sub-option. + * + * parameters: k4aeAuthMan * + */ + /* Use k5 to get the credentials to send in as response */ + tn = (tnParams *)parameters; + so = &tn->subbuffer[SB_TYPE]; + strcpy(server, tn->cname); + server[strlen(server) - 1] = 0; // knock last character off "." + if (k5_auth_send(so[1], server, szUserName, &auth)) + { + tn_sendsub(tn, OPT_AUTHENTICATION, TNQ_NAME, szUserName, strlen(szUserName)); + tn_sendauthsub(tn, OPT_AUTHENTICATION, TNQ_IS, AUTH_KERBEROS_V5, so[1] | AUTH_CLIENT_TO_SERVER, KRB_AUTH, auth.data, auth.length); + } + else + err = 1; + } + break; + + case TNFUNC_AUTH_REPLY: + /* + * Process an [IAC SB] AUTH REPLY <type-modifier-list> [IAC SE] sub-option. + * + * parameters: k4aeAuthMan * + */ + tn = (tnParams *)parameters; + so = &tn->subbuffer[SB_TYPE]; + k5_auth_reply(so[1], tn->subbuffer, tn->sublength); + break; + + default: + err = TNREP_ERROR; + } + + RestoreA4(oldA4); + return err; +} + +/* +** +** K5_auth_send - gets authentication bits we need to send to KDC. +** +** Code lifted from wintel code in the windows directory.) +** (Code lifted from telnet sample code in the appl directory.) +** +** Result is left in auth +** +** Returns: 0 on failure, 1 on success +** +*/ + +static int +k5_auth_send (int how, char *szHostName, char *szUserName, krb5_data *auth) +{ + krb5_error_code r; + krb5_ccache ccache; + krb5_creds cred; + krb5_creds * new_cred; + krb5_flags ap_opts; + int len; + + if (r = krb5_cc_default(k5_context, &ccache)) { + com_err (NULL, r, "while authorizing."); + return(0); + } + + memset((char *)&cred, 0, sizeof(cred)); + if (r = krb5_sname_to_principal(k5_context, szHostName, KRB_SERVICE_NAME, + KRB5_NT_SRV_HST, &cred.server)) { + com_err (NULL, r, "while authorizing."); + return(0); + } + + if (r = krb5_cc_get_principal(k5_context, ccache, &cred.client)) { + com_err (NULL, r, "while authorizing."); + krb5_free_cred_contents(k5_context, &cred); + return(0); + } + if (szUserName[0] == '\0') { /* Get user name now */ + len = krb5_princ_component(k5_context, cred.client, 0)->length; + memcpy (szUserName, + krb5_princ_component(k5_context, cred.client, 0)->data, + len); + szUserName[len] = '\0'; + } + + + if (r = krb5_get_credentials(k5_context, KDC_OPT_RENEWABLE_OK, + ccache, &cred, &new_cred)) { + com_err (NULL, r, "while authorizing."); + krb5_free_cred_contents(k5_context, &cred); + return(0); + } + + ap_opts = 0; + if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) + ap_opts = AP_OPTS_MUTUAL_REQUIRED; + + r = krb5_mk_req_extended(k5_context, (void*) &auth_context, ap_opts, + NULL, new_cred, auth); + + krb5_free_cred_contents(k5_context, &cred); + krb5_free_creds(k5_context, new_cred); + + if (r) { + com_err (NULL, r, "while authorizing."); + return(0); + } + + return(1); +} + +/*+ +** +** K5_auth_reply -- checks the reply for mutual authentication. +** +** Code lifted from telnet sample code in the appl directory. +** +*/ +static int +k5_auth_reply (int how, unsigned char *data, int cnt) { + static int mutual_complete = 0; + char strTmp[100]; + + data += 4; /* Point to status byte */ + + switch (*data++) { + case K5_REJECT: + if (cnt > 0) + sprintf (strTmp, + "Kerberos V5 refuses authentication because %.*s", + cnt, data); + else + sprintf (strTmp, "Kerberos V5 refuses authentication"); + com_err (NULL, 0, strTmp); + + return KFAILURE; + + case K5_ACCEPT: + if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && !mutual_complete) { + sprintf(strTmp, "Kerberos V5 accepted you, " + "but didn't provide mutual authentication"); + com_err (NULL, 0, strTmp); + return KSUCCESS; + } + + return KSUCCESS; + break; + + case K5_RESPONSE: + if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { + /* the rest of the reply should contain a krb_ap_rep */ + krb5_ap_rep_enc_part *reply; + krb5_data inbuf; + krb5_error_code r; + + inbuf.length = cnt; + inbuf.data = (char *)data; + + if (r = krb5_rd_rep (k5_context, (void*) auth_context, &inbuf, &reply)) { + com_err (NULL, r, "while authorizing."); + return KFAILURE; + } + krb5_free_ap_rep_enc_part(k5_context, reply); + + mutual_complete = 1; + } + return KSUCCESS; + + default: + return KSUCCESS; // Unknown reply type + } +} + + +/*+ + * Function: Copy data to buffer, doubling IAC character if present. + * + * Parameters: + * kstream - kstream to send abort message to. + */ +static int +copy_for_net( + unsigned char *to, + unsigned char *from, + int c) +{ + int n; + + n = c; + + while (c-- > 0) { + if ((*to++ = *from++) == IAC) { + n++; + *to++ = IAC; + } + } + + return n; + +} /* copy_for_net */ + + +/* + * Insert a suboption into the suboption buffer. + */ +static void tn_sendsub (tnParams *tn, int code, int request, char *scp, int length) +{ + int len; + unsigned char *src, *lp, *limit; + char start[] = {IAC, SB, 0, 0}; + char end[] = {IAC, SE}; + unsigned char *dst = tn->sendbuffer; + + src = (unsigned char *)scp; + limit = src + length; + start[2] = code; + start[3] = request; + + BlockMoveData(start, dst, sizeof(start)); + dst += sizeof(start); + + /* + * Encode the buffer. IACs must be doubled + */ + if (*src == IAC) { /* check initial iac in buffer */ + *dst++ = IAC; + } + while (src < limit) { + lp = src+1; /* dont check first char */ + while (lp < limit) { /* scan for IAC */ + if (*lp == IAC) + break; + lp++; + } + len = lp - src; + if (lp < limit) /* if stopped on IAC */ + len++; /* include IAC in xmit */ + + BlockMoveData(src, dst, len); + dst += len; + + src = lp; /* resume scanning */ + } + + BlockMoveData(end, dst, 2); + dst += 2; + + len = dst - tn->sendbuffer; + *tn->sendlength -= len; + tn->sendbuffer += len; +} + + +/* + * Insert a suboption into the suboption buffer. + */ +static void tn_sendauthsub (tnParams *tn, int code, int request, int vers, int how, int auth, char *scp, int length) +{ + int len; + unsigned char *src, *lp, *limit; + char start[] = {IAC, SB, 0, 0, 0, 0, 0}; + char end[] = {IAC, SE}; + unsigned char *dst = tn->sendbuffer; + + src = (unsigned char *)scp; + limit = src + length; + start[2] = code; + start[3] = request; + start[4] = vers; + start[5] = how; + start[6] = auth; + + BlockMoveData(start, dst, sizeof(start)); + dst += sizeof(start); + + /* + * Encode the buffer. IACs must be doubled + */ + if (*src == IAC) { /* check initial iac in buffer */ + *dst++ = IAC; + } + while (src < limit) { + lp = src+1; /* dont check first char */ + while (lp < limit) { /* scan for IAC */ + if (*lp == IAC) + break; + lp++; + } + len = lp - src; + if (lp < limit) /* if stopped on IAC */ + len++; /* include IAC in xmit */ + + BlockMoveData(src, dst, len); + dst += len; + + src = lp; /* resume scanning */ + } + + BlockMoveData(end, dst, 2); + dst += 2; + + len = dst - tn->sendbuffer; + *tn->sendlength -= len; + tn->sendbuffer += len; +} + +extern void (*__exit_proc__)(void); +void (*__exit_proc__)(void); diff --git a/src/mac/telnet-k5-auth/telnet-2.7b4-68k.sit.hqx b/src/mac/telnet-k5-auth/telnet-2.7b4-68k.sit.hqx new file mode 100644 index 000000000..fc2497f85 --- /dev/null +++ b/src/mac/telnet-k5-auth/telnet-2.7b4-68k.sit.hqx @@ -0,0 +1,2919 @@ +(This file must be converted with BinHex 4.0) + +:&&4PE'jPG#db,MGL0#df1'XZFfPd!&0*9%46593K!3!!!KqG!!!"4M"m8dP8)3! +#!!)IRA*-BA8#53#3!aC(A`d!&dj$8d%J9'9XEQ9d)$)Z0f)d)#Jf1%XT!*!%!@p +L)0pd!*!6!J6c!*!%rj!%39"36%j$8d%K!+`A$PLX&`jC!!5d+`#3"3)%E3#3"0q +h!*!)-B3'!29GDpI+IQ9QFXV0&-pTFXSTTleR$H58HhELqcjEZG(RDpGP&pZkPZG +hhCV2kD#mBTYjMD)0-Vq*fpMfc#k2ANeXBQkff6E+"hPZ-aEYFK[V-YE+lFh-cmb +3!kK!eAGpJ`E&Vf`%QTmA"Vc%%&`%m`3$BXL"R"bqbma-YLBc-lP46PXj68ijjC6 +6r14QP&01Z31cLh,+cHLQh@Djf@EESlejjDDI#qdb0pYXi@G[EqCQQG[-c)8[E*X +,AfJY(2+&,hcK#emBb"B11@6EK$Zfm)@"20EP`KHHZI$B`M0MQffff@DEC8BjjA3 +!e(F0qVkcE*!!$H,,S$2"Fp)j5"`N4c'hRMSdMc'"-GfD)dchmb1-RA-TqlIIL%* +VL9NXGAUmcMTaD0SP8iH,+CPCPefXJHrNiZ,amSZ$[*["JqIdR-30RLF'rAZfJb2 +mA03"p(2hY$Z[VDqUF9D)*I99GFl"H61FhVTDXDkbV%jdcR$@c",,r(j29APCACA +2+pC@qZSp&@*Y[Gr[UkQVF9jE%8l&B&%6l1190'E@PANVbQU8V-)ZAidi`9Z&Cj0 +SGpEkkQ[+RH*i(l*D@P2,aL)Zjq'+G$[VkQXmJiVm6UpB*YE*,EI3jr8kbq8#Y$M +,Uea9+0F#9#C-J5NkI9'EeZiecYTk$d@XFc*@QMHTe"$+JQ*&IN'@J[mAK@ICeJN +6'1)Cjl#j`P#f8[JTRa"89cpMh9lN-m"SC`kKTc!Vj*SBGVf$6@(hXGHrFC@SNlj +*U*)p!HG8f5rZJ2CSUG0H6Dk('XQM&Yr0CaUh*aMm+2MqF@h`IEc0Am6rk)pGim+ +05Vme1[i-iDprPFSAB`'aD,V4llQ[Di"I&hf[#IjT`K6qC[ZlQ9Xrj+6mA#IC*cq +05%G9p1qMUU[I@bMmMS6hm4kXSHUa(p8GhcFRA-RIC"pXS1UG19qmpF9EKh[*rhr +Aklj6Q3`+8(8UleY[bGqrZ[,8pcrhHJ8pNJ0rqqXJb3'pq09Iq5I3*18meS5rHHY +2f@A9XEkI,$ePjcbS9lJHm&RATkel@lH!6K``-p)4k6#BZVHGYmXBkLEYRa")1%k +[j--KrcKNSZZk3bpfAk(AGkr8kk1hiYfQabrjH+LZ1e$PJlf1RrQVc[AL9D5%3Yr +U(V+k5Xde&F(X298PYJh1%(r`RTLA+6&@#1Hb*8+*6P0d'EF2B`1C9E!eMCRJ,C[ +UFBTe2T'[a4HAPCGDLbqU&E(V0[U`SKH)BUk[ZUc++hV,UTfeiR99(SrSpG@*erP +UTUFKNiIi6$&B08YQFjNMP1&Tl(+@*I4G%C9A898(I'0PA4Rh1je&X#`fXLQ#mLQ +Yi$l6f4@X61Kh0YE&U1KR@'&6P,qXYKB*eihd+(-eJjdRR0FdS0MM,+YeLRRH1QH +0@)L%`N!'`X9+0)l9M!Ak1YDQLl5@HFZG&,pHkC2PJXLDK+[PpF@SBlYK6GITVJ# +SS2V-cj*BkPUeZcbS@k0`GG1&ejKkM+1-QmXmSV1Q"[$$k'XbHU"e,LN*fBXTiKb +P-h@QJ`[NYZCdEVV[6Q4@A8USbe+rXdC'@aD@HDQ0&cL"Cc6j2FikJMd@PCI9Sjr +qU3M6P%kkM"eR0F,3MKiMRppM0HbY8&f5!)QFbG*#G8P#A@B,3jXdep"FUe%b+@$ +EQ%A3Kk)@X"ZBK6h6P9N)XfD8S&2@c@6B`kbbU9HKmcVabLT[KHmk2JR)Gqhrf5- +c5bZT(8a!#&2a-P0(Tk"lQM[EC1aAE9*mZNh1S[bf+(@i4#KKjF+0S6U%80p+B9Q +6rkUUkFlD#d39"V`#mrG+VlI+kaCpp@MDHV(D@HfVQB9KI#(`X3DhXlC1R1@V&i' +Z,FFd[SU2[5Zmj69m(%S@'1*e6PFC)9B52H`[UkQVSX,5h+E1mE*dPS`ZS1V-9@S +d20M1FS4)IBpaa@*KII98Cme)&&VkC!*06eFh2G8BDd+iSUB++i,ZSA,@Pfh9pbj +deY&Z%$CA9'#*@1DX*HpC5RpA!K5EabT#(9$*YV,*JNiABCi+c%mbmD%[-J56[KG +k$jf`X,kQ"V"K)m!aqQA"Hlb3!+lAm+,X96E,M@`A@bI%K[+kPFeQpl"0AA-ML*Q +6,H6V6I+km&RR,%#45d9d-[UUh&2QR5iLcpBUE$RcDjaL498YV8CeD6,Z@XG5f3a +@S%bG1Q5mJMdBXKl#KK95V'0B2eE-(2SqI$Lde[[VHK6Aq-V4#f%j&84BJ`MId[F +XU3IQf&c4SkLqcPm[Phm-fmZ+KF(k(S!bVkafGVLqb&%*JkeQll-(KB'kEPCj8NP +1Gl*EfB0XHeI&Kf"61SAjp&%&2J`-@VJNeK"@bmiAcJ[9+i'04T5Ndp162*HadD% +i8jQIAFAD)icjTF8LqRSCMFMFh[NAFqpD61@PE*X#M+b#pHD3!,@@[BBCR+,6dAk +Rr&pJbcXm+T6T%iq*H",lY6l@5PK`md8KI"DG@XSMR)))BpN@p,rX`fFMmUICql" +Me,VQ!IUNhalKARC8f"HD"hXBl-DlAGe"Nb2-iR@DhR)Qp+VNGKPh"+UhF$X[A)c +jh-RY2050CRahF2Xi*Cpf*JT'f-p@mQR(G#IlH#@IGZERpR18I0VCDQi[8I*TCkh +F,NF$2P3RPqe6ZUm)eZU3!(81V#Y$eM@`EJPCff$G'E,kK(L@)J68,'#Y$PRR`,S +bC&d$kjD3!,80eTdKkaiK&redZbjUA$@"m`9TDA+rlQ%(i&HQLmUEfH%A$Z1L,Sf +`60,SC2Yq&J($Bpa@V(4m0AZ,93MRSP-rUf!CIRDTF%kS[mq$I4&l8*Rh,a$mMGq +Af"fXKEd@kZm[!FrBbUCdpIG3YJJSa$0+)FE#1T@pSPJr!HZLN!$eYf#p)@40`Bc +*BJ[dKN*F&'#e)ca#"[GCm*`#ck'+ChPCqA4hMDrH+d-0aJ6i,i$r%)ZRbMZGeMH ++hF"p@q&l2Ab(KA`Md9hpXCh3EldJX&m)46SGi6%+h[Nc9UcjSbkbS+UL3[(j1[V +S6i6LVRUIMQhS21&(bXbY3)6CL"#I0bfd)h`'2N[*Ti)@0r%jR4RBVl"cD1X2B6f +L@&HcNqbR3UQ5ND'jK6Ne4jYNicIB#IBUGZSK*9KB+h$NX4$R&QT(q`BfM-0XRlj +J4jIcGl!e0-*%&b#dAbNh,+jeG!1#c4S`%lQfq2ceIQbYcI9Lf95J80KAADZ"@G( +[Tm*jJL"F'HVmRl+Hk*LUVNiiNj9KEMcB632NKhkPX&Gaq`hFIL(XRqU`A`%di(2 +#D0L[jrDjX#rTX+q%I9f(r5EB[paK[`2fqc[X$f(lHB+Y-1UZa%pjI!XH2ql`H*! +!IBPTIkkZ@pl--X*IT(1H`54lK@hUL20rXZICfi*&&e2#HbGX$Pr-X,mAXbZ0[G( +%Yl+CcKSXj1(L'UH,Vph'B*E!XYR8TZk+jmAmGN8UNS@+,'RUCDRbPU%*Te8"!rU +F2q4p$lbrfG60#NJ"6DUVm`P8Ch16JBp%br8jC5TrMDeR6f,,dVhj0I6CRl#IGrA +Q5Xb'G8*bU&Im32p1BYZ82H0d@-m*@5GJ(rX-fkG-R&Tdf6,f*efmYE,-khD+Z3U +k%4dqIU!3)pQi*[f%@N*Qe#Tl1Vc1)Dm)0ML"aE)V@8&6lfp6QDjJ&KA!"ZF!5p" +9VF"q-`G)Te299##5fFc4&*h[mpD**9AA1q9Y0C9ji(FlVqF"9*jqI`*Nl#rBfk& +m(X8XfXPZ2Ch2@ZaqZj6"Q`rV*%`#0GPKR41bcS1e*@4G$ZZkN!"e!kaE3YBTf)B +ZC6pTLVc+kI(iVP-Qj@NX"claBfZF6UqmN3f$cqR`LE-l+f526m0M+6`m&Nqph%r +,i(%62")Z,h-$0*N[alN&AVp,FDE-+U2T9"4#TJff@E"*'Uemah%['iXY80mX*,+ +*U-(Vm1lQmd&Ld[ib4mJ0G8N!#&-q5e,3+V+2`cBlUDQ!-,[jRUUb@Jj`m$8Bl4# +0@#J!HXM'`B$R8Um,Na2Eae99GC9!c-,1Q9@eGE5*8@$#a+i'rN#r2f0Yl"@J%cV +hkHM2@L&*&f8(N!#f`LRfYXXe+)*I"IQTL63d6rC,K9mZr,b%53iY91C5)VVqQmS +ZY4J49L0#),GH(ZF)6[FG"a@-jhC"M`fm)S6a5&khXdhBlJph6BS%r,1"hJ`PM+H +`4)&P#-14Q,r"HL41&1Y*%K+"XCiVDC4,Pm@B-D[BLBMHjRSd9h1GA,,FhQCqTfL +`@I#rKIdj`T!!"a$bFrikZ%k9F5&"r"Bhk8ZG0G9B0D4UPBKA#e4@0L'j)&J0G'8 +6faRUZY@)XSNYk+Vf"HMl6l0Ep6%b0%R3k"5ITllD@b[2f`Z3!-1RfC0'EFD`SA* +PmK(a21`XSA$%k99HTa)[$r(14cch8"R$#3"G2)AYD)TAYUfCJ&4a&P&AK@@8'JY +EPAX(,KeSBrF)j`Ma`K9U[9`kYKhfT+jk63'b2)[0$ffL3!pJIlE$[TZ9#ZH&l*G +L&LjQDc[X@f$I'l*r&[kV1r`r#rr9l%$)IJhQakh-'pUZ0X*q6iIpGf$r9ahf"f& +r[-2qCl$rCBIp,f(rASIp*GMhGGKr$2[E(AB2Z`bibeH8+A)5V$RXQ')YJ28mGTd +aBS3*2qR1ZF#%&JZpMGUKUXIRK(5f@-KABYi1khdKkar#qQp8+lEq8U%hZehC)Ri +#a1%Gi3V&QJiF-*IP0HPT+TTEB"bQ`6"iaCYTAj!!I