| 1 | /* |
| 2 | * Copyright 2000, International Business Machines Corporation and others. |
| 3 | * All Rights Reserved. |
| 4 | * |
| 5 | * This software has been released under the terms of the IBM Public |
| 6 | * License. For details, see the LICENSE file in the top-level source |
| 7 | * directory or online at http://www.openafs.org/dl/license10.html |
| 8 | */ |
| 9 | |
| 10 | /* |
| 11 | * ALL RIGHTS RESERVED |
| 12 | */ |
| 13 | |
| 14 | #include <afsconfig.h> |
| 15 | #include <afs/param.h> |
| 16 | #include <afs/stds.h> |
| 17 | |
| 18 | #include <roken.h> |
| 19 | |
| 20 | #include <ctype.h> |
| 21 | |
| 22 | #include <afs/vice.h> |
| 23 | #include <afs/cmd.h> |
| 24 | #include <afs/auth.h> |
| 25 | #include <afs/afsutil.h> |
| 26 | #include <afs/sys_prototypes.h> |
| 27 | |
| 28 | /* |
| 29 | Modifications: |
| 30 | |
| 31 | 29 Oct 1992 Patch GetTokens to print something reasonable when there are no tokens. |
| 32 | |
| 33 | */ |
| 34 | |
| 35 | /* this is a structure used to communicate with the afs cache mgr, but is |
| 36 | * otherwise irrelevant, or worse. |
| 37 | */ |
| 38 | struct ClearToken { |
| 39 | afs_int32 AuthHandle; |
| 40 | char HandShakeKey[8]; |
| 41 | afs_int32 ViceId; |
| 42 | afs_int32 BeginTimestamp; |
| 43 | afs_int32 EndTimestamp; |
| 44 | }; |
| 45 | |
| 46 | |
| 47 | static int |
| 48 | SetSysname(afs_int32 ahost, afs_int32 auid, char *sysname) |
| 49 | { |
| 50 | afs_int32 code; |
| 51 | afs_int32 pheader[6]; |
| 52 | char space[1200], *tp; |
| 53 | struct ViceIoctl blob; |
| 54 | afs_int32 setp = 1; |
| 55 | |
| 56 | /* otherwise we've got the token, now prepare to build the pioctl args */ |
| 57 | pheader[0] = ahost; |
| 58 | pheader[1] = auid; |
| 59 | pheader[2] = 0; /* group low */ |
| 60 | pheader[3] = 0; /* group high */ |
| 61 | pheader[4] = 38 /*VIOC_AFS_SYSNAME */ ; /* sysname pioctl index */ |
| 62 | pheader[5] = 1; /* NFS protocol exporter # */ |
| 63 | |
| 64 | /* copy stuff in */ |
| 65 | memcpy(space, pheader, sizeof(pheader)); |
| 66 | tp = space + sizeof(pheader); |
| 67 | |
| 68 | /* finally setup the pioctl call's parameters */ |
| 69 | blob.in_size = sizeof(pheader); |
| 70 | blob.in = space; |
| 71 | blob.out_size = 0; |
| 72 | blob.out = NULL; |
| 73 | memcpy(tp, &setp, sizeof(afs_int32)); |
| 74 | tp += sizeof(afs_int32); |
| 75 | strcpy(tp, sysname); |
| 76 | blob.in_size += sizeof(afs_int32) + strlen(sysname) + 1; |
| 77 | tp += strlen(sysname); |
| 78 | *(tp++) = '\0'; |
| 79 | code = pioctl(NULL, _VICEIOCTL(99), &blob, 0); |
| 80 | if (code) { |
| 81 | code = errno; |
| 82 | } |
| 83 | return code; |
| 84 | } |
| 85 | |
| 86 | |
| 87 | static int |
| 88 | GetTokens(afs_int32 ahost, afs_int32 auid) |
| 89 | { |
| 90 | struct ViceIoctl iob; |
| 91 | afs_int32 pheader[6]; |
| 92 | char tbuffer[1024]; |
| 93 | afs_int32 code = 0; |
| 94 | int index, newIndex; |
| 95 | char *stp; /* secret token ptr */ |
| 96 | struct ClearToken ct; |
| 97 | char *tp; |
| 98 | afs_int32 temp, gotit = 0; |
| 99 | int maxLen; /* biggest ticket we can copy */ |
| 100 | int tktLen; /* server ticket length */ |
| 101 | time_t tokenExpireTime; |
| 102 | char UserName[MAXKTCNAMELEN + MAXKTCNAMELEN]; |
| 103 | struct ktc_token token; |
| 104 | struct ktc_principal clientName; |
| 105 | time_t current_time; |
| 106 | char *expireString; |
| 107 | |
| 108 | current_time = time(0); |
| 109 | |
| 110 | /* otherwise we've got the token, now prepare to build the pioctl args */ |
| 111 | pheader[0] = ahost; |
| 112 | pheader[1] = auid; |
| 113 | pheader[2] = 0; /* group low */ |
| 114 | pheader[3] = 0; /* group high */ |
| 115 | pheader[4] = 8; /* gettoken pioctl index */ |
| 116 | pheader[5] = 1; /* NFS protocol exporter # */ |
| 117 | |
| 118 | for (index = 0; index < 200; index++) { /* sanity check in case pioctl fails */ |
| 119 | code = ktc_ListTokens(index, &newIndex, &clientName); |
| 120 | if (code) { |
| 121 | if (code == KTC_NOENT) { |
| 122 | /* all done */ |
| 123 | if (!gotit) |
| 124 | printf("knfs: there are no tokens here.\n"); |
| 125 | code = 0; |
| 126 | } |
| 127 | break; /* done, but failed */ |
| 128 | } |
| 129 | if (strcmp(clientName.name, "afs") != 0) |
| 130 | continue; /* wrong ticket service */ |
| 131 | |
| 132 | /* copy stuff in */ |
| 133 | memcpy(tbuffer, pheader, sizeof(pheader)); |
| 134 | tp = tbuffer + sizeof(pheader); |
| 135 | memcpy(tp, &index, sizeof(afs_int32)); |
| 136 | tp += sizeof(afs_int32); |
| 137 | iob.in = tbuffer; |
| 138 | iob.in_size = sizeof(afs_int32) + sizeof(pheader); |
| 139 | iob.out = tbuffer; |
| 140 | iob.out_size = sizeof(tbuffer); |
| 141 | code = pioctl(NULL, _VICEIOCTL(99), &iob, 0); |
| 142 | if (code < 0 && errno == EDOM) |
| 143 | return KTC_NOENT; |
| 144 | else if (code == 0) { |
| 145 | /* check to see if this is the right cell/realm */ |
| 146 | tp = tbuffer; |
| 147 | memcpy(&temp, tp, sizeof(afs_int32)); /* get size of secret token */ |
| 148 | tktLen = temp; /* remember size of ticket */ |
| 149 | tp += sizeof(afs_int32); |
| 150 | stp = tp; /* remember where ticket is, for later */ |
| 151 | tp += temp; /* skip ticket for now */ |
| 152 | memcpy(&temp, tp, sizeof(afs_int32)); /* get size of clear token */ |
| 153 | if (temp != sizeof(struct ClearToken)) |
| 154 | return KTC_ERROR; |
| 155 | tp += sizeof(afs_int32); /* skip length */ |
| 156 | memcpy(&ct, tp, temp); /* copy token for later use */ |
| 157 | tp += temp; /* skip clear token itself */ |
| 158 | tp += sizeof(afs_int32); /* skip primary flag */ |
| 159 | /* tp now points to the cell name */ |
| 160 | if (strcmp(tp, clientName.cell) == 0) { |
| 161 | /* closing in now, we've got the right cell */ |
| 162 | gotit = 1; |
| 163 | maxLen = |
| 164 | sizeof(token) - sizeof(struct ktc_token) + |
| 165 | MAXKTCTICKETLEN; |
| 166 | if (tktLen < 0 || tktLen > maxLen) |
| 167 | return KTC_TOOBIG; |
| 168 | memcpy(token.ticket, stp, tktLen); |
| 169 | token.startTime = ct.BeginTimestamp; |
| 170 | token.endTime = ct.EndTimestamp; |
| 171 | if (ct.AuthHandle == -1) |
| 172 | ct.AuthHandle = 999; |
| 173 | token.kvno = ct.AuthHandle; |
| 174 | memcpy(&token.sessionKey, ct.HandShakeKey, |
| 175 | sizeof(struct ktc_encryptionKey)); |
| 176 | token.ticketLen = tktLen; |
| 177 | if ((token.kvno == 999) || /* old style bcrypt ticket */ |
| 178 | (ct.BeginTimestamp && /* new w/ prserver lookup */ |
| 179 | (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1))) { |
| 180 | sprintf(clientName.name, "AFS ID %d", ct.ViceId); |
| 181 | clientName.instance[0] = 0; |
| 182 | } else { |
| 183 | sprintf(clientName.name, "Unix UID %d", ct.ViceId); |
| 184 | clientName.instance[0] = 0; |
| 185 | } |
| 186 | strlcpy(clientName.cell, tp, sizeof(clientName.cell)); |
| 187 | |
| 188 | tokenExpireTime = token.endTime; |
| 189 | strlcpy(UserName, clientName.name, sizeof(UserName)); |
| 190 | if (clientName.instance[0] != 0) { |
| 191 | strlcat(UserName, ".", sizeof(UserName)); |
| 192 | strlcat(UserName, clientName.instance, sizeof(UserName)); |
| 193 | } |
| 194 | if (UserName[0] == 0) |
| 195 | printf("Tokens"); |
| 196 | else if (strncmp(UserName, "AFS ID", 6) == 0) { |
| 197 | printf("User's (%s) tokens", UserName); |
| 198 | } else if (strncmp(UserName, "Unix UID", 8) == 0) { |
| 199 | printf("Tokens"); |
| 200 | } else |
| 201 | printf("User %s's tokens", UserName); |
| 202 | printf(" for %s%s%s@%s ", clientName.name, |
| 203 | clientName.instance[0] ? "." : "", clientName.instance, |
| 204 | clientName.cell); |
| 205 | if (tokenExpireTime <= current_time) |
| 206 | printf("[>> Expired <<]\n"); |
| 207 | else { |
| 208 | expireString = ctime(&tokenExpireTime); |
| 209 | expireString += 4; /*Move past the day of week */ |
| 210 | expireString[12] = '\0'; |
| 211 | printf("[Expires %s]\n", expireString); |
| 212 | } |
| 213 | |
| 214 | } |
| 215 | } |
| 216 | } |
| 217 | return code; |
| 218 | } |
| 219 | |
| 220 | |
| 221 | static int |
| 222 | NFSUnlog(afs_int32 ahost, afs_int32 auid) |
| 223 | { |
| 224 | afs_int32 code; |
| 225 | afs_int32 pheader[6]; |
| 226 | char space[1200]; |
| 227 | struct ViceIoctl blob; |
| 228 | |
| 229 | /* otherwise we've got the token, now prepare to build the pioctl args */ |
| 230 | pheader[0] = ahost; |
| 231 | pheader[1] = auid; |
| 232 | pheader[2] = 0; /* group low */ |
| 233 | pheader[3] = 0; /* group high */ |
| 234 | pheader[4] = 9; /* unlog pioctl index */ |
| 235 | pheader[5] = 1; /* NFS protocol exporter # */ |
| 236 | |
| 237 | /* copy stuff in */ |
| 238 | memcpy(space, pheader, sizeof(pheader)); |
| 239 | |
| 240 | /* finally setup the pioctl call's parameters */ |
| 241 | blob.in_size = sizeof(pheader); |
| 242 | blob.in = space; |
| 243 | blob.out_size = 0; |
| 244 | blob.out = NULL; |
| 245 | code = pioctl(NULL, _VICEIOCTL(99), &blob, 0); |
| 246 | if (code) { |
| 247 | code = errno; |
| 248 | } |
| 249 | return code; |
| 250 | } |
| 251 | |
| 252 | /* Copy the AFS service token into the kernel for a particular host and user */ |
| 253 | static int |
| 254 | NFSCopyToken(afs_int32 ahost, afs_int32 auid) |
| 255 | { |
| 256 | struct ktc_principal client, server; |
| 257 | struct ktc_token theTicket; |
| 258 | afs_int32 code; |
| 259 | afs_int32 pheader[6]; |
| 260 | char space[1200]; |
| 261 | struct ClearToken ct; |
| 262 | afs_int32 index, newIndex; |
| 263 | afs_int32 temp; /* for bcopy */ |
| 264 | char *tp; |
| 265 | struct ViceIoctl blob; |
| 266 | |
| 267 | for (index = 0;; index = newIndex) { |
| 268 | code = ktc_ListTokens(index, &newIndex, &server); |
| 269 | if (code) { |
| 270 | if (code == KTC_NOENT) { |
| 271 | /* all done */ |
| 272 | code = 0; |
| 273 | } |
| 274 | break; /* done, but failed */ |
| 275 | } |
| 276 | if (strcmp(server.name, "afs") != 0) |
| 277 | continue; /* wrong ticket service */ |
| 278 | code = ktc_GetToken(&server, &theTicket, sizeof(theTicket), &client); |
| 279 | if (code) |
| 280 | return code; |
| 281 | |
| 282 | /* otherwise we've got the token, now prepare to build the pioctl args */ |
| 283 | pheader[0] = ahost; |
| 284 | pheader[1] = auid; |
| 285 | pheader[2] = 0; /* group low */ |
| 286 | pheader[3] = 0; /* group high */ |
| 287 | pheader[4] = 3; /* set token pioctl index */ |
| 288 | pheader[5] = 1; /* NFS protocol exporter # */ |
| 289 | |
| 290 | /* copy in the header */ |
| 291 | memcpy(space, pheader, sizeof(pheader)); |
| 292 | tp = space + sizeof(pheader); |
| 293 | /* copy in the size of the encrypted part */ |
| 294 | memcpy(tp, &theTicket.ticketLen, sizeof(afs_int32)); |
| 295 | tp += sizeof(afs_int32); |
| 296 | /* copy in the ticket itself */ |
| 297 | memcpy(tp, theTicket.ticket, theTicket.ticketLen); |
| 298 | tp += theTicket.ticketLen; |
| 299 | /* copy in "clear token"'s size */ |
| 300 | temp = sizeof(struct ClearToken); |
| 301 | memcpy(tp, &temp, sizeof(afs_int32)); |
| 302 | tp += sizeof(afs_int32); |
| 303 | /* create the clear token and copy *it* in */ |
| 304 | ct.AuthHandle = theTicket.kvno; /* where we hide the key version # */ |
| 305 | memcpy(ct.HandShakeKey, &theTicket.sessionKey, |
| 306 | sizeof(ct.HandShakeKey)); |
| 307 | |
| 308 | ct.ViceId = auid; |
| 309 | ct.BeginTimestamp = theTicket.startTime; |
| 310 | ct.EndTimestamp = theTicket.endTime; |
| 311 | memcpy(tp, &ct, sizeof(ct)); |
| 312 | tp += sizeof(ct); |
| 313 | /* copy in obsolete primary flag */ |
| 314 | temp = 0; |
| 315 | memcpy(tp, &temp, sizeof(afs_int32)); |
| 316 | tp += sizeof(afs_int32); |
| 317 | /* copy in cell name, null terminated */ |
| 318 | strcpy(tp, server.cell); |
| 319 | tp += strlen(server.cell) + 1; |
| 320 | |
| 321 | /* finally setup the pioctl call's parameters */ |
| 322 | blob.in_size = tp - space; |
| 323 | blob.in = space; |
| 324 | blob.out_size = 0; |
| 325 | blob.out = NULL; |
| 326 | code = pioctl(NULL, _VICEIOCTL(99), &blob, 0); |
| 327 | if (code) { |
| 328 | code = errno; |
| 329 | break; |
| 330 | } |
| 331 | } |
| 332 | return code; |
| 333 | } |
| 334 | |
| 335 | static int |
| 336 | cmdproc(struct cmd_syndesc *as, void *arock) |
| 337 | { |
| 338 | struct hostent *the; |
| 339 | char *tp, *sysname = 0; |
| 340 | afs_int32 uid, addr; |
| 341 | afs_int32 code; |
| 342 | |
| 343 | the = (struct hostent *) |
| 344 | hostutil_GetHostByName(tp = as->parms[0].items->data); |
| 345 | if (!the) { |
| 346 | printf("knfs: unknown host '%s'.\n", tp); |
| 347 | return -1; |
| 348 | } |
| 349 | memcpy(&addr, the->h_addr, sizeof(afs_int32)); |
| 350 | uid = -1; |
| 351 | if (as->parms[1].items) { |
| 352 | code = util_GetInt32(tp = as->parms[1].items->data, &uid); |
| 353 | if (code) { |
| 354 | printf("knfs: can't parse '%s' as a number (UID)\n", tp); |
| 355 | return code; |
| 356 | } |
| 357 | } else |
| 358 | uid = -1; /* means wildcard: match any user on this host */ |
| 359 | |
| 360 | /* |
| 361 | * If not "-id" is passed then we use the getuid() id, unless it's root |
| 362 | * that is doing it in which case we only authenticate as "system:anyuser" |
| 363 | * as it's appropriate for root. (The cm handles conversions from 0 to |
| 364 | * "afs_nobody"!) |
| 365 | */ |
| 366 | if (uid == -1) { |
| 367 | uid = getuid(); |
| 368 | } |
| 369 | |
| 370 | if (as->parms[2].items) { |
| 371 | sysname = as->parms[2].items->data; |
| 372 | } |
| 373 | |
| 374 | if (as->parms[4].items) { |
| 375 | /* tokens specified */ |
| 376 | code = GetTokens(addr, uid); |
| 377 | if (code) { |
| 378 | if (code == ENOEXEC) |
| 379 | printf |
| 380 | ("knfs: Translator in 'passwd sync' mode; remote uid must be the same as local uid\n"); |
| 381 | else |
| 382 | printf("knfs: failed to get tokens for uid %d (code %d)\n", |
| 383 | uid, code); |
| 384 | } |
| 385 | return code; |
| 386 | } |
| 387 | |
| 388 | /* finally, parsing is done, make the call */ |
| 389 | if (as->parms[3].items) { |
| 390 | /* unlog specified */ |
| 391 | code = NFSUnlog(addr, uid); |
| 392 | if (code) { |
| 393 | if (code == ENOEXEC) |
| 394 | printf |
| 395 | ("knfs: Translator in 'passwd sync' mode; remote uid must be the same as local uid\n"); |
| 396 | else |
| 397 | printf("knfs: failed to unlog (code %d)\n", code); |
| 398 | } |
| 399 | } else { |
| 400 | code = NFSCopyToken(addr, uid); |
| 401 | if (code) { |
| 402 | if (code == ENOEXEC) |
| 403 | printf |
| 404 | ("knfs: Translator in 'passwd sync' mode; remote uid must be the same as local uid\n"); |
| 405 | else |
| 406 | printf("knfs: failed to copy tokens (code %d)\n", code); |
| 407 | } |
| 408 | if (sysname) { |
| 409 | code = SetSysname(addr, uid, sysname); |
| 410 | if (code) { |
| 411 | printf("knfs: failed to set client's @sys to %s (code %d)\n", |
| 412 | sysname, code); |
| 413 | } |
| 414 | } |
| 415 | } |
| 416 | return code; |
| 417 | } |
| 418 | |
| 419 | #include "AFS_component_version_number.c" |
| 420 | |
| 421 | int |
| 422 | main(int argc, char **argv) |
| 423 | { |
| 424 | struct cmd_syndesc *ts; |
| 425 | afs_int32 code; |
| 426 | |
| 427 | #ifdef AFS_AIX32_ENV |
| 428 | /* |
| 429 | * The following signal action for AIX is necessary so that in case of a |
| 430 | * crash (i.e. core is generated) we can include the user's data section |
| 431 | * in the core dump. Unfortunately, by default, only a partial core is |
| 432 | * generated which, in many cases, isn't too useful. |
| 433 | */ |
| 434 | struct sigaction nsa; |
| 435 | |
| 436 | sigemptyset(&nsa.sa_mask); |
| 437 | nsa.sa_handler = SIG_DFL; |
| 438 | nsa.sa_flags = SA_FULLDUMP; |
| 439 | sigaction(SIGABRT, &nsa, NULL); |
| 440 | sigaction(SIGSEGV, &nsa, NULL); |
| 441 | #endif |
| 442 | |
| 443 | ts = cmd_CreateSyntax(NULL, cmdproc, NULL, 0, "copy tickets for NFS"); |
| 444 | cmd_AddParm(ts, "-host", CMD_SINGLE, CMD_REQUIRED, "host name"); |
| 445 | cmd_AddParm(ts, "-id", CMD_SINGLE, CMD_OPTIONAL, "user ID (decimal)"); |
| 446 | cmd_AddParm(ts, "-sysname", CMD_SINGLE, CMD_OPTIONAL, |
| 447 | "host's '@sys' value"); |
| 448 | cmd_AddParm(ts, "-unlog", CMD_FLAG, CMD_OPTIONAL, "unlog remote user"); |
| 449 | cmd_AddParm(ts, "-tokens", CMD_FLAG, CMD_OPTIONAL, |
| 450 | "display all tokens for remote [host,id]"); |
| 451 | |
| 452 | code = cmd_Dispatch(argc, argv); |
| 453 | return code; |
| 454 | } |