Produced by Araxis Merge on 11/20/2017 2:16:17 PM GMT Standard Time. See www.araxis.com for information about Merge. This report uses XHTML and CSS2, and is best viewed with a modern standards-compliant browser. For optimum results when printing this report, use landscape orientation and enable printing of background images and colours in your browser.
| # | Location | File | Last Modified |
|---|---|---|---|
| 1 | C:\Merge Test Files\8.0.47\java\org\apache\catalina\authenticator | SpnegoAuthenticator.java | Fri Sep 29 16:53:28 2017 UTC |
| 2 | C:\Merge Test Files\8.5.23\java\org\apache\catalina\authenticator | SpnegoAuthenticator.java | Thu Sep 28 11:32:16 2017 UTC |
| Description | Between Files 1 and 2 |
|
|---|---|---|
| Text Blocks | Lines | |
| Unchanged | 14 | 968 |
| Changed | 12 | 24 |
| Inserted | 1 | 1 |
| Removed | 0 | 0 |
| Whitespace | Consecutive whitespace is treated as a single space |
|---|---|
| Character case | Differences in character case are significant |
| Line endings | Differences in line endings (CR and LF characters) are ignored |
| CR/LF characters | Not shown in the comparison detail |
No regular expressions were active.
| 1 | /* | 1 | /* | |||
| 2 | * License d to the A pache Soft ware Found ation (ASF ) under on e or more | 2 | * License d to the A pache Soft ware Found ation (ASF ) under on e or more | |||
| 3 | * contrib utor licen se agreeme nts. See the NOTICE file dist ributed wi th | 3 | * contrib utor licen se agreeme nts. See the NOTICE file dist ributed wi th | |||
| 4 | * this wo rk for add itional in formation regarding copyright ownership. | 4 | * this wo rk for add itional in formation regarding copyright ownership. | |||
| 5 | * The ASF licenses this file to You und er the Apa che Licens e, Version 2.0 | 5 | * The ASF licenses this file to You und er the Apa che Licens e, Version 2.0 | |||
| 6 | * (the "L icense"); you may no t use this file exce pt in comp liance wit h | 6 | * (the "L icense"); you may no t use this file exce pt in comp liance wit h | |||
| 7 | * the Lic ense. You may obtai n a copy o f the Lice nse at | 7 | * the Lic ense. You may obtai n a copy o f the Lice nse at | |||
| 8 | * | 8 | * | |||
| 9 | * ht tp://www.a pache.org/ licenses/L ICENSE-2.0 | 9 | * ht tp://www.a pache.org/ licenses/L ICENSE-2.0 | |||
| 10 | * | 10 | * | |||
| 11 | * Unless required b y applicab le law or agreed to in writing , software | 11 | * Unless required b y applicab le law or agreed to in writing , software | |||
| 12 | * distrib uted under the Licen se is dist ributed on an "AS IS " BASIS, | 12 | * distrib uted under the Licen se is dist ributed on an "AS IS " BASIS, | |||
| 13 | * WITHOUT WARRANTIE S OR CONDI TIONS OF A NY KIND, e ither expr ess or imp lied. | 13 | * WITHOUT WARRANTIE S OR CONDI TIONS OF A NY KIND, e ither expr ess or imp lied. | |||
| 14 | * See the License f or the spe cific lang uage gover ning permi ssions and | 14 | * See the License f or the spe cific lang uage gover ning permi ssions and | |||
| 15 | * limitat ions under the Licen se. | 15 | * limitat ions under the Licen se. | |||
| 16 | */ | 16 | */ | |||
| 17 | package or g.apache.c atalina.au thenticato r; | 17 | package or g.apache.c atalina.au thenticato r; | |||
| 18 | 18 | |||||
| 19 | import jav a.io.File; | 19 | import jav a.io.File; | |||
| 20 | import jav a.io.IOExc eption; | 20 | import jav a.io.IOExc eption; | |||
| 21 | import jav a.security .Principal ; | 21 | import jav a.security .Principal ; | |||
| 22 | import jav a.security .Privilege dAction; | 22 | import jav a.security .Privilege dAction; | |||
| 23 | import jav a.security .Privilege dActionExc eption; | 23 | import jav a.security .Privilege dActionExc eption; | |||
| 24 | import jav a.security .Privilege dException Action; | 24 | import jav a.security .Privilege dException Action; | |||
| 25 | import jav a.util.Lin kedHashMap ; | 25 | import jav a.util.Lin kedHashMap ; | |||
| 26 | import jav a.util.reg ex.Pattern ; | 26 | import jav a.util.reg ex.Pattern ; | |||
| 27 | 27 | |||||
| 28 | import jav ax.securit y.auth.Sub ject; | 28 | import jav ax.securit y.auth.Sub ject; | |||
| 29 | import jav ax.securit y.auth.log in.LoginCo ntext; | 29 | import jav ax.securit y.auth.log in.LoginCo ntext; | |||
| 30 | import jav ax.securit y.auth.log in.LoginEx ception; | 30 | import jav ax.securit y.auth.log in.LoginEx ception; | |||
| 31 | import jav ax.servlet .http.Http ServletRes ponse; | 31 | import jav ax.servlet .http.Http ServletRes ponse; | |||
| 32 | 32 | |||||
| 33 | import org .apache.ca talina.Lif ecycleExce ption; | 33 | import org .apache.ca talina.Lif ecycleExce ption; | |||
| 34 | import org .apache.ca talina.Rea lm; | 34 | import org .apache.ca talina.Rea lm; | |||
| 35 | import org .apache.ca talina.con nector.Req uest; | 35 | import org .apache.ca talina.con nector.Req uest; | |||
| 36 | import org .apache.ju li.logging .Log; | 36 | import org .apache.ju li.logging .Log; | |||
| 37 | import org .apache.ju li.logging .LogFactor y; | 37 | import org .apache.ju li.logging .LogFactor y; | |||
| 38 | import org .apache.to mcat.util. buf.ByteCh unk; | 38 | import org .apache.to mcat.util. buf.ByteCh unk; | |||
| 39 | import org .apache.to mcat.util. buf.Messag eBytes; | 39 | import org .apache.to mcat.util. buf.Messag eBytes; | |||
| 40 | import org .apache.to mcat.util. codec.bina ry.Base64; | 40 | import org .apache.to mcat.util. codec.bina ry.Base64; | |||
| 41 | import org .apache.to mcat.util. compat.Jre Vendor; | 41 | import org .apache.to mcat.util. compat.Jre Vendor; | |||
| 42 | import org .ietf.jgss .GSSContex t; | 42 | import org .ietf.jgss .GSSContex t; | |||
| 43 | import org .ietf.jgss .GSSCreden tial; | 43 | import org .ietf.jgss .GSSCreden tial; | |||
| 44 | import org .ietf.jgss .GSSExcept ion; | 44 | import org .ietf.jgss .GSSExcept ion; | |||
| 45 | import org .ietf.jgss .GSSManage r; | 45 | import org .ietf.jgss .GSSManage r; | |||
| 46 | import org .ietf.jgss .Oid; | 46 | import org .ietf.jgss .Oid; | |||
| 47 | 47 | |||||
| 48 | 48 | |||||
| 49 | /** | 49 | /** | |||
| 50 | * A SPNEG O authenti cator that uses the SPNEGO/Ker beros supp ort built in to Java | 50 | * A SPNEG O authenti cator that uses the SPNEGO/Ker beros supp ort built in to Java | |||
| 51 | * 6. Succ essful Ker beros auth entication depends o n the corr ect config uration of | 51 | * 6. Succ essful Ker beros auth entication depends o n the corr ect config uration of | |||
| 52 | * multipl e componen ts. If the configura tion is in valid, the error mes sages are | 52 | * multipl e componen ts. If the configura tion is in valid, the error mes sages are | |||
| 53 | * often c ryptic alt hough a Go ogle searc h will usu ally point you in th e right | 53 | * often c ryptic alt hough a Go ogle searc h will usu ally point you in th e right | |||
| 54 | * directi on. | 54 | * directi on. | |||
| 55 | */ | 55 | */ | |||
| 56 | public cla ss SpnegoA uthenticat or extends Authentic atorBase { | 56 | public cla ss SpnegoA uthenticat or extends Authentic atorBase { | |||
| 57 | 57 | |||||
| 58 | privat e static f inal Log l og = LogFa ctory.getL og(SpnegoA uthenticat or.class); | 58 | privat e static f inal Log l og = LogFa ctory.getL og(SpnegoA uthenticat or.class); | |||
| 59 | privat e static f inal Strin g AUTH_HEA DER_VALUE_ NEGOTIATE = "Negotia te"; | |||||
| 59 | 60 | |||||
| 60 | privat e String l oginConfig Name = Con stants.DEF AULT_LOGIN _MODULE_NA ME; | 61 | privat e String l oginConfig Name = Con stants.DEF AULT_LOGIN _MODULE_NA ME; | |||
| 61 | public String ge tLoginConf igName() { | 62 | public String ge tLoginConf igName() { | |||
| 62 | re turn login ConfigName ; | 63 | re turn login ConfigName ; | |||
| 63 | } | 64 | } | |||
| 64 | public void setL oginConfig Name(Strin g loginCon figName) { | 65 | public void setL oginConfig Name(Strin g loginCon figName) { | |||
| 65 | th is.loginCo nfigName = loginConf igName; | 66 | th is.loginCo nfigName = loginConf igName; | |||
| 66 | } | 67 | } | |||
| 67 | 68 | |||||
| 68 | privat e boolean storeDeleg atedCreden tial = tru e; | 69 | privat e boolean storeDeleg atedCreden tial = tru e; | |||
| 69 | public boolean i sStoreDele gatedCrede ntial() { | 70 | public boolean i sStoreDele gatedCrede ntial() { | |||
| 70 | re turn store DelegatedC redential; | 71 | re turn store DelegatedC redential; | |||
| 71 | } | 72 | } | |||
| 72 | public void setS toreDelega tedCredent ial( | 73 | public void setS toreDelega tedCredent ial( | |||
| 73 | boolean storeDeleg atedCreden tial) { | 74 | boolean storeDeleg atedCreden tial) { | |||
| 74 | th is.storeDe legatedCre dential = storeDeleg atedCreden tial; | 75 | th is.storeDe legatedCre dential = storeDeleg atedCreden tial; | |||
| 75 | } | 76 | } | |||
| 76 | 77 | |||||
| 77 | privat e Pattern noKeepAliv eUserAgent s = null; | 78 | privat e Pattern noKeepAliv eUserAgent s = null; | |||
| 78 | public String ge tNoKeepAli veUserAgen ts() { | 79 | public String ge tNoKeepAli veUserAgen ts() { | |||
| 79 | Pa ttern p = noKeepAliv eUserAgent s; | 80 | Pa ttern p = noKeepAliv eUserAgent s; | |||
| 80 | if (p == nul l) { | 81 | if (p == nul l) { | |||
| 81 | return n ull; | 82 | return n ull; | |||
| 82 | } else { | 83 | } else { | |||
| 83 | return p .pattern() ; | 84 | return p .pattern() ; | |||
| 84 | } | 85 | } | |||
| 85 | } | 86 | } | |||
| 86 | public void setN oKeepAlive UserAgents (String no KeepAliveU serAgents) { | 87 | public void setN oKeepAlive UserAgents (String no KeepAliveU serAgents) { | |||
| 87 | if (noKeepAl iveUserAge nts == nul l || | 88 | if (noKeepAl iveUserAge nts == nul l || | |||
| 88 | noKe epAliveUse rAgents.le ngth() == 0) { | 89 | noKe epAliveUse rAgents.le ngth() == 0) { | |||
| 89 | this.noK eepAliveUs erAgents = null; | 90 | this.noK eepAliveUs erAgents = null; | |||
| 90 | } else { | 91 | } else { | |||
| 91 | this.noK eepAliveUs erAgents = Pattern.c ompile(noK eepAliveUs erAgents); | 92 | this.noK eepAliveUs erAgents = Pattern.c ompile(noK eepAliveUs erAgents); | |||
| 92 | } | 93 | } | |||
| 93 | } | 94 | } | |||
| 94 | 95 | |||||
| 95 | privat e boolean applyJava8 u40Fix = t rue; | 96 | privat e boolean applyJava8 u40Fix = t rue; | |||
| 96 | public boolean g etApplyJav a8u40Fix() { | 97 | public boolean g etApplyJav a8u40Fix() { | |||
| 97 | re turn apply Java8u40Fi x; | 98 | re turn apply Java8u40Fi x; | |||
| 98 | } | 99 | } | |||
| 99 | public void setA pplyJava8u 40Fix(bool ean applyJ ava8u40Fix ) { | 100 | public void setA pplyJava8u 40Fix(bool ean applyJ ava8u40Fix ) { | |||
| 100 | th is.applyJa va8u40Fix = applyJav a8u40Fix; | 101 | th is.applyJa va8u40Fix = applyJav a8u40Fix; | |||
| 101 | } | 102 | } | |||
| 102 | 103 | |||||
| 103 | 104 | |||||
| 104 | @Overr ide | 105 | @Overr ide | |||
| 105 | protec ted String getAuthMe thod() { | 106 | protec ted String getAuthMe thod() { | |||
| 106 | re turn Const ants.SPNEG O_METHOD; | 107 | re turn Const ants.SPNEG O_METHOD; | |||
| 107 | } | 108 | } | |||
| 108 | 109 | |||||
| 109 | 110 | |||||
| 110 | @Overr ide | 111 | @Overr ide | |||
| 111 | protec ted void i nitInterna l() throws Lifecycle Exception { | 112 | protec ted void i nitInterna l() throws Lifecycle Exception { | |||
| 112 | su per.initIn ternal(); | 113 | su per.initIn ternal(); | |||
| 113 | 114 | |||||
| 114 | // Kerberos configurat ion file l ocation | 115 | // Kerberos configurat ion file l ocation | |||
| 115 | St ring krb5C onf = Syst em.getProp erty(Const ants.KRB5_ CONF_PROPE RTY); | 116 | St ring krb5C onf = Syst em.getProp erty(Const ants.KRB5_ CONF_PROPE RTY); | |||
| 116 | if (krb5Conf == null) { | 117 | if (krb5Conf == null) { | |||
| 117 | // Syste m property not set, use the To mcat defau lt | 118 | // Syste m property not set, use the To mcat defau lt | |||
| 118 | File krb 5ConfFile = new File (container .getCatali naBase(), | 119 | File krb 5ConfFile = new File (container .getCatali naBase(), | |||
| 119 | Constants. DEFAULT_KR B5_CONF); | 120 | Constants. DEFAULT_KR B5_CONF); | |||
| 120 | System.s etProperty (Constants .KRB5_CONF _PROPERTY, | 121 | System.s etProperty (Constants .KRB5_CONF _PROPERTY, | |||
| 121 | krb5ConfFi le.getAbso lutePath() ); | 122 | krb5ConfFi le.getAbso lutePath() ); | |||
| 122 | } | 123 | } | |||
| 123 | 124 | |||||
| 124 | // JAAS conf iguration file locat ion | 125 | // JAAS conf iguration file locat ion | |||
| 125 | St ring jaasC onf = Syst em.getProp erty(Const ants.JAAS_ CONF_PROPE RTY); | 126 | St ring jaasC onf = Syst em.getProp erty(Const ants.JAAS_ CONF_PROPE RTY); | |||
| 126 | if (jaasConf == null) { | 127 | if (jaasConf == null) { | |||
| 127 | // Syste m property not set, use the To mcat defau lt | 128 | // Syste m property not set, use the To mcat defau lt | |||
| 128 | File jaa sConfFile = new File (container .getCatali naBase(), | 129 | File jaa sConfFile = new File (container .getCatali naBase(), | |||
| 129 | Constants. DEFAULT_JA AS_CONF); | 130 | Constants. DEFAULT_JA AS_CONF); | |||
| 130 | System.s etProperty (Constants .JAAS_CONF _PROPERTY, | 131 | System.s etProperty (Constants .JAAS_CONF _PROPERTY, | |||
| 131 | jaasConfFi le.getAbso lutePath() ); | 132 | jaasConfFi le.getAbso lutePath() ); | |||
| 132 | } | 133 | } | |||
| 133 | } | 134 | } | |||
| 134 | 135 | |||||
| 135 | 136 | |||||
| 136 | @Overr ide | 137 | @Overr ide | |||
| 137 |
p
ubli
c
|
138 | p rote c ted boolean doA uthenticat e(Request request, H ttpServlet Response r esponse) | |||
| 138 | throws I OException { | 139 | throws I OException { | |||
| 139 | 140 | |||||
| 140 | if (checkFor CachedAuth entication (request, response, true)) { | 141 | if (checkFor CachedAuth entication (request, response, true)) { | |||
| 141 | return t rue; | 142 | return t rue; | |||
| 142 | } | 143 | } | |||
| 143 | 144 | |||||
| 144 | Me ssageBytes authoriza tion = | 145 | Me ssageBytes authoriza tion = | |||
| 145 | request. getCoyoteR equest().g etMimeHead ers() | 146 | request. getCoyoteR equest().g etMimeHead ers() | |||
| 146 | .getValu e("authori zation"); | 147 | .getValu e("authori zation"); | |||
| 147 | 148 | |||||
| 148 | if (authoriz ation == n ull) { | 149 | if (authoriz ation == n ull) { | |||
| 149 | if (log. isDebugEna bled()) { | 150 | if (log. isDebugEna bled()) { | |||
| 150 | log. debug(sm.g etString(" authentica tor.noAuth Header")); | 151 | log. debug(sm.g etString(" authentica tor.noAuth Header")); | |||
| 151 | } | 152 | } | |||
| 152 | response .setHeader ("WWW-Auth enticate", "Negotiat e"); | 153 | response .setHeader (AUTH_HEAD ER_NAME, A UTH_HEADER _VALUE_NEG OTIATE); | |||
| 153 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | 154 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | |||
| 154 | return f alse; | 155 | return f alse; | |||
| 155 | } | 156 | } | |||
| 156 | 157 | |||||
| 157 | au thorizatio n.toBytes( ); | 158 | au thorizatio n.toBytes( ); | |||
| 158 | By teChunk au thorizatio nBC = auth orization. getByteChu nk(); | 159 | By teChunk au thorizatio nBC = auth orization. getByteChu nk(); | |||
| 159 | 160 | |||||
| 160 | if (!authori zationBC.s tartsWithI gnoreCase( "negotiate ", 0)) { | 161 | if (!authori zationBC.s tartsWithI gnoreCase( "negotiate ", 0)) { | |||
| 161 | if (log. isDebugEna bled()) { | 162 | if (log. isDebugEna bled()) { | |||
| 162 | log. debug(sm.g etString( | 163 | log. debug(sm.g etString( | |||
| 163 | "spneg oAuthentic ator.authH eaderNotNe go")); | 164 | "spneg oAuthentic ator.authH eaderNotNe go")); | |||
| 164 | } | 165 | } | |||
| 165 | response .setHeader ("WWW-Auth enticate", "Negotiat e"); | 166 | response .setHeader (AUTH_HEAD ER_NAME, A UTH_HEADER _VALUE_NEG OTIATE); | |||
| 166 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | 167 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | |||
| 167 | return f alse; | 168 | return f alse; | |||
| 168 | } | 169 | } | |||
| 169 | 170 | |||||
| 170 | au thorizatio nBC.setOff set(author izationBC. getOffset( ) + 10); | 171 | au thorizatio nBC.setOff set(author izationBC. getOffset( ) + 10); | |||
| 171 | 172 | |||||
| 172 | by te[] decod ed = Base6 4.decodeBa se64(autho rizationBC .getBuffer (), | 173 | by te[] decod ed = Base6 4.decodeBa se64(autho rizationBC .getBuffer (), | |||
| 173 | auth orizationB C.getOffse t(), | 174 | auth orizationB C.getOffse t(), | |||
| 174 | auth orizationB C.getLengt h()); | 175 | auth orizationB C.getLengt h()); | |||
| 175 | 176 | |||||
| 176 | if (getApply Java8u40Fi x()) { | 177 | if (getApply Java8u40Fi x()) { | |||
| 177 | SpnegoTo kenFixer.f ix(decoded ); | 178 | SpnegoTo kenFixer.f ix(decoded ); | |||
| 178 | } | 179 | } | |||
| 179 | 180 | |||||
| 180 | if (decoded. length == 0) { | 181 | if (decoded. length == 0) { | |||
| 181 | if (log. isDebugEna bled()) { | 182 | if (log. isDebugEna bled()) { | |||
| 182 | log. debug(sm.g etString( | 183 | log. debug(sm.g etString( | |||
| 183 | "spneg oAuthentic ator.authH eaderNoTok en")); | 184 | "spneg oAuthentic ator.authH eaderNoTok en")); | |||
| 184 | } | 185 | } | |||
| 185 | response .setHeader ("WWW-Auth enticate", "Negotiat e"); | 186 | response .setHeader (AUTH_HEAD ER_NAME, A UTH_HEADER _VALUE_NEG OTIATE); | |||
| 186 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | 187 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | |||
| 187 | return f alse; | 188 | return f alse; | |||
| 188 | } | 189 | } | |||
| 189 | 190 | |||||
| 190 | Lo ginContext lc = null ; | 191 | Lo ginContext lc = null ; | |||
| 191 | GS SContext g ssContext = null; | 192 | GS SContext g ssContext = null; | |||
| 192 | by te[] outTo ken = null ; | 193 | by te[] outTo ken = null ; | |||
| 193 | Pr incipal pr incipal = null; | 194 | Pr incipal pr incipal = null; | |||
| 194 | tr y { | 195 | tr y { | |||
| 195 | try { | 196 | try { | |||
| 196 | lc = new Login Context(ge tLoginConf igName()); | 197 | lc = new Login Context(ge tLoginConf igName()); | |||
| 197 | lc.l ogin(); | 198 | lc.l ogin(); | |||
| 198 | } catch (LoginExce ption e) { | 199 | } catch (LoginExce ption e) { | |||
| 199 | log. error(sm.g etString(" spnegoAuth enticator. serviceLog inFail"), | 200 | log. error(sm.g etString(" spnegoAuth enticator. serviceLog inFail"), | |||
| 200 | e); | 201 | e); | |||
| 201 | resp onse.sendE rror( | 202 | resp onse.sendE rror( | |||
| 202 | HttpSe rvletRespo nse.SC_INT ERNAL_SERV ER_ERROR); | 203 | HttpSe rvletRespo nse.SC_INT ERNAL_SERV ER_ERROR); | |||
| 203 | retu rn false; | 204 | retu rn false; | |||
| 204 | } | 205 | } | |||
| 205 | 206 | |||||
| 206 | Subject subject = lc.getSubj ect(); | 207 | Subject subject = lc.getSubj ect(); | |||
| 207 | 208 | |||||
| 208 | // Assum e the GSSC ontext is stateless | 209 | // Assum e the GSSC ontext is stateless | |||
| 209 | // TODO: Confirm t his assump tion | 210 | // TODO: Confirm t his assump tion | |||
| 210 | final GS SManager m anager = G SSManager. getInstanc e(); | 211 | final GS SManager m anager = G SSManager. getInstanc e(); | |||
| 211 | // IBM J DK only un derstands indefinite lifetime | 212 | // IBM J DK only un derstands indefinite lifetime | |||
| 212 | final in t credenti alLifetime ; | 213 | final in t credenti alLifetime ; | |||
| 213 | if (JreV endor.IS_I BM_JVM) { | 214 | if (JreV endor.IS_I BM_JVM) { | |||
| 214 | cred entialLife time = GSS Credential .INDEFINIT E_LIFETIME ; | 215 | cred entialLife time = GSS Credential .INDEFINIT E_LIFETIME ; | |||
| 215 | } else { | 216 | } else { | |||
| 216 | cred entialLife time = GSS Credential .DEFAULT_L IFETIME; | 217 | cred entialLife time = GSS Credential .DEFAULT_L IFETIME; | |||
| 217 | } | 218 | } | |||
| 218 | final Pr ivilegedEx ceptionAct ion<GSSCre dential> a ction = | 219 | final Pr ivilegedEx ceptionAct ion<GSSCre dential> a ction = | |||
| 219 | new Privileged ExceptionA ction<GSSC redential> () { | 220 | new Privileged ExceptionA ction<GSSC redential> () { | |||
| 220 | @Override | 221 | @Override | |||
| 221 | public GSS Credential run() thr ows GSSExc eption { | 222 | public GSS Credential run() thr ows GSSExc eption { | |||
| 222 | return manager.c reateCrede ntial(null , | 223 | return manager.c reateCrede ntial(null , | |||
| 223 | credenti alLifetime , | 224 | credenti alLifetime , | |||
| 224 | new Oid( "1.3.6.1.5 .5.2"), | 225 | new Oid( "1.3.6.1.5 .5.2"), | |||
| 225 | GSSCrede ntial.ACCE PT_ONLY); | 226 | GSSCrede ntial.ACCE PT_ONLY); | |||
| 226 | } | 227 | } | |||
| 227 | }; | 228 | }; | |||
| 228 | gssConte xt = manag er.createC ontext(Sub ject.doAs( subject, a ction)); | 229 | gssConte xt = manag er.createC ontext(Sub ject.doAs( subject, a ction)); | |||
| 229 | 230 | |||||
| 230 | outToken = Subject .doAs(lc.g etSubject( ), new Acc eptAction( gssContext , decoded) ); | 231 | outToken = Subject .doAs(lc.g etSubject( ), new Acc eptAction( gssContext , decoded) ); | |||
| 231 | 232 | |||||
| 232 | if (outT oken == nu ll) { | 233 | if (outT oken == nu ll) { | |||
| 233 | if ( log.isDebu gEnabled() ) { | 234 | if ( log.isDebu gEnabled() ) { | |||
| 234 | log.debug( sm.getStri ng( | 235 | log.debug( sm.getStri ng( | |||
| 235 | "s pnegoAuthe nticator.t icketValid ateFail")) ; | 236 | "s pnegoAuthe nticator.t icketValid ateFail")) ; | |||
| 236 | } | 237 | } | |||
| 237 | // S tart again | 238 | // S tart again | |||
| 238 | resp onse.setHe ader("WWW- Authentica te", "Nego tiate"); | 239 | resp onse.setHe ader(AUTH_ HEADER_NAM E, AUTH_HE ADER_VALUE _NEGOTIATE ); | |||
| 239 | resp onse.sendE rror(HttpS ervletResp onse.SC_UN AUTHORIZED ); | 240 | resp onse.sendE rror(HttpS ervletResp onse.SC_UN AUTHORIZED ); | |||
| 240 | retu rn false; | 241 | retu rn false; | |||
| 241 | } | 242 | } | |||
| 242 | 243 | |||||
| 243 | principa l = Subjec t.doAs(sub ject, new Authentica teAction( | 244 | principa l = Subjec t.doAs(sub ject, new Authentica teAction( | |||
| 244 | context.ge tRealm(), gssContext , storeDel egatedCred ential)); | 245 | context.ge tRealm(), gssContext , storeDel egatedCred ential)); | |||
| 245 | 246 | |||||
| 246 | } catch (GSS Exception e) { | 247 | } catch (GSS Exception e) { | |||
| 247 | if (log. isDebugEna bled()) { | 248 | if (log. isDebugEna bled()) { | |||
| 248 | log. debug(sm.g etString(" spnegoAuth enticator. ticketVali dateFail") , e); | 249 | log. debug(sm.g etString(" spnegoAuth enticator. ticketVali dateFail") , e); | |||
| 249 | } | 250 | } | |||
| 250 | response .setHeader ("WWW-Auth enticate", "Negotiat e"); | 251 | response .setHeader (AUTH_HEAD ER_NAME, A UTH_HEADER _VALUE_NEG OTIATE); | |||
| 251 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | 252 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | |||
| 252 | return f alse; | 253 | return f alse; | |||
| 253 | } catch (Pri vilegedAct ionExcepti on e) { | 254 | } catch (Pri vilegedAct ionExcepti on e) { | |||
| 254 | Throwabl e cause = e.getCause (); | 255 | Throwabl e cause = e.getCause (); | |||
| 255 | if (caus e instance of GSSExce ption) { | 256 | if (caus e instance of GSSExce ption) { | |||
| 256 | if ( log.isDebu gEnabled() ) { | 257 | if ( log.isDebu gEnabled() ) { | |||
| 257 | log.debug( sm.getStri ng("spnego Authentica tor.servic eLoginFail "), e); | 258 | log.debug( sm.getStri ng("spnego Authentica tor.servic eLoginFail "), e); | |||
| 258 | } | 259 | } | |||
| 259 | } else { | 260 | } else { | |||
| 260 | log. error(sm.g etString(" spnegoAuth enticator. serviceLog inFail"), e); | 261 | log. error(sm.g etString(" spnegoAuth enticator. serviceLog inFail"), e); | |||
| 261 | } | 262 | } | |||
| 262 | response .setHeader ("WWW-Auth enticate", "Negotiat e"); | 263 | response .setHeader (AUTH_HEAD ER_NAME, A UTH_HEADER _VALUE_NEG OTIATE); | |||
| 263 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | 264 | response .sendError (HttpServl etResponse .SC_UNAUTH ORIZED); | |||
| 264 | return f alse; | 265 | return f alse; | |||
| 265 | } finally { | 266 | } finally { | |||
| 266 | if (gssC ontext != null) { | 267 | if (gssC ontext != null) { | |||
| 267 | try { | 268 | try { | |||
| 268 | gssContext .dispose() ; | 269 | gssContext .dispose() ; | |||
| 269 | } ca tch (GSSEx ception e) { | 270 | } ca tch (GSSEx ception e) { | |||
| 270 | // Ignore | 271 | // Ignore | |||
| 271 | } | 272 | } | |||
| 272 | } | 273 | } | |||
| 273 | if (lc ! = null) { | 274 | if (lc ! = null) { | |||
| 274 | try { | 275 | try { | |||
| 275 | lc.logout( ); | 276 | lc.logout( ); | |||
| 276 | } ca tch (Login Exception e) { | 277 | } ca tch (Login Exception e) { | |||
| 277 | // Ignore | 278 | // Ignore | |||
| 278 | } | 279 | } | |||
| 279 | } | 280 | } | |||
| 280 | } | 281 | } | |||
| 281 | 282 | |||||
| 282 | // Send resp onse token on succes s and fail ure | 283 | // Send resp onse token on succes s and fail ure | |||
| 283 | re sponse.set Header("WW W-Authenti cate", "Ne gotiate " | 284 | re sponse.set Header(AUT H_HEADER_N AME, AUTH_ HEADER_VAL UE_NEGOTIA TE + " " | |||
| 284 | + Ba se64.encod eBase64Str ing(outTok en)); | 285 | + Ba se64.encod eBase64Str ing(outTok en)); | |||
| 285 | 286 | |||||
| 286 | if (principa l != null) { | 287 | if (principa l != null) { | |||
| 287 | register (request, response, principal, Constants .SPNEGO_ME THOD, | 288 | register (request, response, principal, Constants .SPNEGO_ME THOD, | |||
| 288 | principal. getName(), null); | 289 | principal. getName(), null); | |||
| 289 | 290 | |||||
| 290 | Pattern p = noKeep AliveUserA gents; | 291 | Pattern p = noKeep AliveUserA gents; | |||
| 291 | if (p != null) { | 292 | if (p != null) { | |||
| 292 | Mess ageBytes u a = | 293 | Mess ageBytes u a = | |||
| 293 | reques t.getCoyot eRequest() .getMimeHe aders().ge tValue( | 294 | reques t.getCoyot eRequest() .getMimeHe aders().ge tValue( | |||
| 294 | "user-ag ent"); | 295 | "user-ag ent"); | |||
| 295 | if ( ua != null && p.matc her(ua.toS tring()).m atches()) { | 296 | if ( ua != null && p.matc her(ua.toS tring()).m atches()) { | |||
| 296 | response.s etHeader(" Connection ", "close" ); | 297 | response.s etHeader(" Connection ", "close" ); | |||
| 297 | } | 298 | } | |||
| 298 | } | 299 | } | |||
| 299 | return t rue; | 300 | return t rue; | |||
| 300 | } | 301 | } | |||
| 301 | 302 | |||||
| 302 | re sponse.sen dError(Htt pServletRe sponse.SC_ UNAUTHORIZ ED); | 303 | re sponse.sen dError(Htt pServletRe sponse.SC_ UNAUTHORIZ ED); | |||
| 303 | re turn false ; | 304 | re turn false ; | |||
| 304 | } | 305 | } | |||
| 305 | 306 | |||||
| 306 | 307 | |||||
| 307 | /** | 308 | /** | |||
| 308 | * Thi s class ge ts a gss c redential via a priv ileged act ion. | 309 | * Thi s class ge ts a gss c redential via a priv ileged act ion. | |||
| 309 | */ | 310 | */ | |||
| 310 | p r i vate static cl ass Accept Action imp lements Pr ivilegedEx ceptionAct ion<byte[] > { | 311 | p ubl i c static cl ass Accept Action imp lements Pr ivilegedEx ceptionAct ion<byte[] > { | |||
| 311 | 312 | |||||
| 312 | GS SContext g ssContext; | 313 | GS SContext g ssContext; | |||
| 313 | 314 | |||||
| 314 | by te[] decod ed; | 315 | by te[] decod ed; | |||
| 315 | 316 | |||||
| 316 |
|
317 | public AcceptActi on(GSSCont ext contex t, byte[] decodedTok en) { | |||
| 317 | this.gss Context = context; | 318 | this.gss Context = context; | |||
| 318 | this.dec oded = dec odedToken; | 319 | this.dec oded = dec odedToken; | |||
| 319 | } | 320 | } | |||
| 320 | 321 | |||||
| 321 | @O verride | 322 | @O verride | |||
| 322 | pu blic byte[ ] run() th rows GSSEx ception { | 323 | pu blic byte[ ] run() th rows GSSEx ception { | |||
| 323 | return g ssContext. acceptSecC ontext(dec oded, | 324 | return g ssContext. acceptSecC ontext(dec oded, | |||
| 324 | 0, decoded .length); | 325 | 0, decoded .length); | |||
| 325 | } | 326 | } | |||
| 326 | } | 327 | } | |||
| 327 | 328 | |||||
| 328 | 329 | |||||
| 329 | p r i vate static cl ass Authen ticateActi on impleme nts Privil egedAction <Principal > { | 330 | p ubl i c static cl ass Authen ticateActi on impleme nts Privil egedAction <Principal > { | |||
| 330 | 331 | |||||
| 331 | pr ivate fina l Realm re alm; | 332 | pr ivate fina l Realm re alm; | |||
| 332 | pr ivate fina l GSSConte xt gssCont ext; | 333 | pr ivate fina l GSSConte xt gssCont ext; | |||
| 333 | pr ivate fina l boolean storeDeleg atedCreden tial; | 334 | pr ivate fina l boolean storeDeleg atedCreden tial; | |||
| 334 | 335 | |||||
| 335 | pu blic Authe nticateAct ion(Realm realm, GSS Context gs sContext, | 336 | pu blic Authe nticateAct ion(Realm realm, GSS Context gs sContext, | |||
| 336 | bool ean storeD elegatedCr edential) { | 337 | bool ean storeD elegatedCr edential) { | |||
| 337 | this.rea lm = realm ; | 338 | this.rea lm = realm ; | |||
| 338 | this.gss Context = gssContext ; | 339 | this.gss Context = gssContext ; | |||
| 339 | this.sto reDelegate dCredentia l = storeD elegatedCr edential; | 340 | this.sto reDelegate dCredentia l = storeD elegatedCr edential; | |||
| 340 | } | 341 | } | |||
| 341 | 342 | |||||
| 342 | @O verride | 343 | @O verride | |||
| 343 | pu blic Princ ipal run() { | 344 | pu blic Princ ipal run() { | |||
| 344 | return r ealm.authe nticate(gs sContext, storeDeleg atedCreden tial); | 345 | return r ealm.authe nticate(gs sContext, storeDeleg atedCreden tial); | |||
| 345 | } | 346 | } | |||
| 346 | } | 347 | } | |||
| 347 | 348 | |||||
| 348 | 349 | |||||
| 349 | /** | 350 | /** | |||
| 350 | * Thi s class im plements a hack arou nd an inco mpatibilit y between the | 351 | * Thi s class im plements a hack arou nd an inco mpatibilit y between the | |||
| 351 | * SPN EGO implem entation i n Windows and the SP NEGO imple mentation in Java 8 | 352 | * SPN EGO implem entation i n Windows and the SP NEGO imple mentation in Java 8 | |||
| 352 | * upd ate 40 onw ards. It w as introdu ced by the change to fix this bug: | 353 | * upd ate 40 onw ards. It w as introdu ced by the change to fix this bug: | |||
| 353 | * htt ps://bugs. openjdk.ja va.net/bro wse/JDK-80 48194 | 354 | * htt ps://bugs. openjdk.ja va.net/bro wse/JDK-80 48194 | |||
| 354 | * (no te: the ch ange appli ed is not the one su ggested in the bug r eport) | 355 | * (no te: the ch ange appli ed is not the one su ggested in the bug r eport) | |||
| 355 | * <p> | 356 | * <p> | |||
| 356 | * It is not cle ar to me i f Windows, Java or T omcat is a t fault he re. I | 357 | * It is not cle ar to me i f Windows, Java or T omcat is a t fault he re. I | |||
| 357 | * thi nk it is J ava but I could be w rong. | 358 | * thi nk it is J ava but I could be w rong. | |||
| 358 | * <p> | 359 | * <p> | |||
| 359 | * Thi s hack wor ks by re-o rdering th e list of mechTypes in the Neg TokenInit | 360 | * Thi s hack wor ks by re-o rdering th e list of mechTypes in the Neg TokenInit | |||
| 360 | * tok en. | 361 | * tok en. | |||
| 361 | */ | 362 | */ | |||
| 362 | p r i vate static cl ass Spnego TokenFixer { | 363 | p ubl i c static cl ass Spnego TokenFixer { | |||
| 363 | 364 | |||||
| 364 | pu blic stati c void fix (byte[] to ken) { | 365 | pu blic stati c void fix (byte[] to ken) { | |||
| 365 | SpnegoTo kenFixer f ixer = new SpnegoTok enFixer(to ken); | 366 | SpnegoTo kenFixer f ixer = new SpnegoTok enFixer(to ken); | |||
| 366 | fixer.fi x(); | 367 | fixer.fi x(); | |||
| 367 | } | 368 | } | |||
| 368 | 369 | |||||
| 369 | 370 | |||||
| 370 | pr ivate fina l byte[] t oken; | 371 | pr ivate fina l byte[] t oken; | |||
| 371 | pr ivate int pos = 0; | 372 | pr ivate int pos = 0; | |||
| 372 | 373 | |||||
| 373 | 374 | |||||
| 374 | pr ivate Spne goTokenFix er(byte[] token) { | 375 | pr ivate Spne goTokenFix er(byte[] token) { | |||
| 375 | this.tok en = token ; | 376 | this.tok en = token ; | |||
| 376 | } | 377 | } | |||
| 377 | 378 | |||||
| 378 | 379 | |||||
| 379 | // Fixes the token in- place | 380 | // Fixes the token in- place | |||
| 380 | pr ivate void fix() { | 381 | pr ivate void fix() { | |||
| 381 | /* | 382 | /* | |||
| 382 | * Usefu l referenc es: | 383 | * Usefu l referenc es: | |||
| 383 | * http: //tools.ie tf.org/htm l/rfc4121# page-5 | 384 | * http: //tools.ie tf.org/htm l/rfc4121# page-5 | |||
| 384 | * http: //tools.ie tf.org/htm l/rfc2743# page-81 | 385 | * http: //tools.ie tf.org/htm l/rfc2743# page-81 | |||
| 385 | * https ://msdn.mi crosoft.co m/en-us/li brary/ms99 5330.aspx | 386 | * https ://msdn.mi crosoft.co m/en-us/li brary/ms99 5330.aspx | |||
| 386 | */ | 387 | */ | |||
| 387 | 388 | |||||
| 388 | // Scan until we f ind the me ch types l ist. If we find anyt hing | 389 | // Scan until we f ind the me ch types l ist. If we find anyt hing | |||
| 389 | // unexp ected, abo rt the fix process. | 390 | // unexp ected, abo rt the fix process. | |||
| 390 | if (!tag (0x60)) re turn; | 391 | if (!tag (0x60)) re turn; | |||
| 391 | if (!len gth()) ret urn; | 392 | if (!len gth()) ret urn; | |||
| 392 | if (!oid ("1.3.6.1. 5.5.2")) r eturn; | 393 | if (!oid ("1.3.6.1. 5.5.2")) r eturn; | |||
| 393 | if (!tag (0xa0)) re turn; | 394 | if (!tag (0xa0)) re turn; | |||
| 394 | if (!len gth()) ret urn; | 395 | if (!len gth()) ret urn; | |||
| 395 | if (!tag (0x30)) re turn; | 396 | if (!tag (0x30)) re turn; | |||
| 396 | if (!len gth()) ret urn; | 397 | if (!len gth()) ret urn; | |||
| 397 | if (!tag (0xa0)) re turn; | 398 | if (!tag (0xa0)) re turn; | |||
| 398 | lengthAs Int(); | 399 | lengthAs Int(); | |||
| 399 | if (!tag (0x30)) re turn; | 400 | if (!tag (0x30)) re turn; | |||
| 400 | // Now a t the star t of the m echType li st. | 401 | // Now a t the star t of the m echType li st. | |||
| 401 | // Read the mechTy pes into a n ordered set | 402 | // Read the mechTy pes into a n ordered set | |||
| 402 | int mech TypesLen = lengthAsI nt(); | 403 | int mech TypesLen = lengthAsI nt(); | |||
| 403 | int mech TypesStart = pos; | 404 | int mech TypesStart = pos; | |||
| 404 | LinkedHa shMap<Stri ng, int[]> mechTypeE ntries = n ew LinkedH ashMap<>() ; | 405 | LinkedHa shMap<Stri ng, int[]> mechTypeE ntries = n ew LinkedH ashMap<>() ; | |||
| 405 | while (p os < mechT ypesStart + mechType sLen) { | 406 | while (p os < mechT ypesStart + mechType sLen) { | |||
| 406 | int[ ] value = new int[2] ; | 407 | int[ ] value = new int[2] ; | |||
| 407 | valu e[0] = pos ; | 408 | valu e[0] = pos ; | |||
| 408 | Stri ng key = o idAsString (); | 409 | Stri ng key = o idAsString (); | |||
| 409 | valu e[1] = pos - value[0 ]; | 410 | valu e[1] = pos - value[0 ]; | |||
| 410 | mech TypeEntrie s.put(key, value); | 411 | mech TypeEntrie s.put(key, value); | |||
| 411 | } | 412 | } | |||
| 412 | // Now c onstruct t he re-orde red mechTy pe list | 413 | // Now c onstruct t he re-orde red mechTy pe list | |||
| 413 | byte[] r eplacement = new byt e[mechType sLen]; | 414 | byte[] r eplacement = new byt e[mechType sLen]; | |||
| 414 | int repl acementPos = 0; | 415 | int repl acementPos = 0; | |||
| 415 | 416 | |||||
| 416 | int[] fi rst = mech TypeEntrie s.remove(" 1.2.840.11 3554.1.2.2 "); | 417 | int[] fi rst = mech TypeEntrie s.remove(" 1.2.840.11 3554.1.2.2 "); | |||
| 417 | if (firs t != null) { | 418 | if (firs t != null) { | |||
| 418 | Syst em.arrayco py(token, first[0], replacemen t, replace mentPos, f irst[1]); | 419 | Syst em.arrayco py(token, first[0], replacemen t, replace mentPos, f irst[1]); | |||
| 419 | repl acementPos += first[ 1]; | 420 | repl acementPos += first[ 1]; | |||
| 420 | } | 421 | } | |||
| 421 | for (int [] markers : mechTyp eEntries.v alues()) { | 422 | for (int [] markers : mechTyp eEntries.v alues()) { | |||
| 422 | Syst em.arrayco py(token, markers[0] , replacem ent, repla cementPos, markers[1 ]); | 423 | Syst em.arrayco py(token, markers[0] , replacem ent, repla cementPos, markers[1 ]); | |||
| 423 | repl acementPos += marker s[1]; | 424 | repl acementPos += marker s[1]; | |||
| 424 | } | 425 | } | |||
| 425 | 426 | |||||
| 426 | // Final ly, replac e the orig inal mechT ype list w ith the re -ordered | 427 | // Final ly, replac e the orig inal mechT ype list w ith the re -ordered | |||
| 427 | // one. | 428 | // one. | |||
| 428 | System.a rraycopy(r eplacement , 0, token , mechType sStart, me chTypesLen ); | 429 | System.a rraycopy(r eplacement , 0, token , mechType sStart, me chTypesLen ); | |||
| 429 | } | 430 | } | |||
| 430 | 431 | |||||
| 431 | 432 | |||||
| 432 | pr ivate bool ean tag(in t expected ) { | 433 | pr ivate bool ean tag(in t expected ) { | |||
| 433 | return ( token[pos+ +] & 0xFF) == expect ed; | 434 | return ( token[pos+ +] & 0xFF) == expect ed; | |||
| 434 | } | 435 | } | |||
| 435 | 436 | |||||
| 436 | 437 | |||||
| 437 | pr ivate bool ean length () { | 438 | pr ivate bool ean length () { | |||
| 438 | // No ne ed to reta in the len gth - just need to c onsume it and make | 439 | // No ne ed to reta in the len gth - just need to c onsume it and make | |||
| 439 | // sure it is vali d. | 440 | // sure it is vali d. | |||
| 440 | int len = lengthAs Int(); | 441 | int len = lengthAs Int(); | |||
| 441 | return p os + len = = token.le ngth; | 442 | return p os + len = = token.le ngth; | |||
| 442 | } | 443 | } | |||
| 443 | 444 | |||||
| 444 | 445 | |||||
| 445 | pr ivate int lengthAsIn t() { | 446 | pr ivate int lengthAsIn t() { | |||
| 446 | int len = token[po s++] & 0xF F; | 447 | int len = token[po s++] & 0xF F; | |||
| 447 | if (len > 127) { | 448 | if (len > 127) { | |||
| 448 | int bytes = le n - 128; | 449 | int bytes = le n - 128; | |||
| 449 | len = 0; | 450 | len = 0; | |||
| 450 | for (int i = 0 ; i < byte s; i++) { | 451 | for (int i = 0 ; i < byte s; i++) { | |||
| 451 | len = len << 8; | 452 | len = len << 8; | |||
| 452 | len = len + (token[p os++] & 0x ff); | 453 | len = len + (token[p os++] & 0x ff); | |||
| 453 | } | 454 | } | |||
| 454 | } | 455 | } | |||
| 455 | return l en; | 456 | return l en; | |||
| 456 | } | 457 | } | |||
| 457 | 458 | |||||
| 458 | 459 | |||||
| 459 | pr ivate bool ean oid(St ring expec ted) { | 460 | pr ivate bool ean oid(St ring expec ted) { | |||
| 460 | return e xpected.eq uals(oidAs String()); | 461 | return e xpected.eq uals(oidAs String()); | |||
| 461 | } | 462 | } | |||
| 462 | 463 | |||||
| 463 | 464 | |||||
| 464 | pr ivate Stri ng oidAsSt ring() { | 465 | pr ivate Stri ng oidAsSt ring() { | |||
| 465 | if (!tag (0x06)) re turn null; | 466 | if (!tag (0x06)) re turn null; | |||
| 466 | StringBu ilder resu lt = new S tringBuild er(); | 467 | StringBu ilder resu lt = new S tringBuild er(); | |||
| 467 | int len = lengthAs Int(); | 468 | int len = lengthAs Int(); | |||
| 468 | // First byte is s pecial cas e | 469 | // First byte is s pecial cas e | |||
| 469 | int v = token[pos+ +] & 0xFF; | 470 | int v = token[pos+ +] & 0xFF; | |||
| 470 | int c2 = v % 40; | 471 | int c2 = v % 40; | |||
| 471 | int c1 = (v - c2) / 40; | 472 | int c1 = (v - c2) / 40; | |||
| 472 | result.a ppend(c1); | 473 | result.a ppend(c1); | |||
| 473 | result.a ppend('.') ; | 474 | result.a ppend('.') ; | |||
| 474 | result.a ppend(c2); | 475 | result.a ppend(c2); | |||
| 475 | int c = 0; | 476 | int c = 0; | |||
| 476 | boolean write = fa lse; | 477 | boolean write = fa lse; | |||
| 477 | for (int i = 1; i < len; i++ ) { | 478 | for (int i = 1; i < len; i++ ) { | |||
| 478 | int b = token[ pos++] & 0 xFF; | 479 | int b = token[ pos++] & 0 xFF; | |||
| 479 | if ( b > 127) { | 480 | if ( b > 127) { | |||
| 480 | b -= 128; | 481 | b -= 128; | |||
| 481 | } el se { | 482 | } el se { | |||
| 482 | write = tr ue; | 483 | write = tr ue; | |||
| 483 | } | 484 | } | |||
| 484 | c = c << 7; | 485 | c = c << 7; | |||
| 485 | c += b; | 486 | c += b; | |||
| 486 | if ( write) { | 487 | if ( write) { | |||
| 487 | result.app end('.'); | 488 | result.app end('.'); | |||
| 488 | result.app end(c); | 489 | result.app end(c); | |||
| 489 | c = 0; | 490 | c = 0; | |||
| 490 | write = fa lse; | 491 | write = fa lse; | |||
| 491 | } | 492 | } | |||
| 492 | } | 493 | } | |||
| 493 | return r esult.toSt ring(); | 494 | return r esult.toSt ring(); | |||
| 494 | } | 495 | } | |||
| 495 | } | 496 | } | |||
| 496 | } | 497 | } |
Araxis Merge (but not the data content of this report) is Copyright © 1993–2017 Araxis Ltd (www.araxis.com). All rights reserved.